REST API Reference
Base URL: https://app.marqov.ai (production) or http://localhost:3000 (development)
All authenticated endpoints require a Supabase session cookie. Responses are JSON. Errors return { "error": "message" }.
Authentication
POST /api/auth/validate-code
Validate an invitation code for platform access. Public endpoint — no authentication required.
Rate limit: 5 requests/minute per IP.
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Invitation code (1-64 chars, alphanumeric + hyphens/underscores) |
Response (200):
| Field | Type | Description |
|---|---|---|
valid | boolean | Whether the code is valid |
remaining_uses | number | Number of remaining uses for this code |
Error codes: 400 (invalid code), 429 (rate limited), 500 (server error).
Jobs
POST /api/jobs/submit
Submit a quantum job for execution. Requires authentication.
Rate limits:
- User: 10 requests/minute
- Team daily quota: 100 jobs/day
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
script_id | UUID | One of script_id, script_slug, or inline_code | Script UUID |
script_slug | string | See above | Script slug (e.g., bell-state) |
inline_code | string | See above | Raw code to execute (max 50,000 chars) |
framework | string | No | qiskit, cirq, pennylane, openqasm, or marqov |
backend | string | Yes | Backend slug (see GET /api/backends) |
params | object | Yes | Execution parameters (see below) |
params.shots | integer | Yes | Number of measurement shots (1-100,000) |
params.theta | number | No | VQE rotation angle |
params.graph_size | integer | No | QAOA graph size (2-20) |
params.layers | integer | No | QAOA layers (1-10) |
params.noise_model | object | No | Noise model for noisy backends |
create_capsule | boolean | No | Create a reproducibility capsule (default: false) |
capsule_name | string | No | Name for the capsule (1-200 chars) |
execution_mode | string | No | direct or temporal (default: direct) |
Response (200):
| Field | Type | Description |
|---|---|---|
job_id | UUID | Created job run identifier |
Error codes: 400 (validation), 401 (unauthorized), 402 (budget exceeded), 404 (script not found), 429 (rate limited), 500 (server error).
402 response includes:
| Field | Type | Description |
|---|---|---|
error | string | Reason for budget rejection |
estimated_cost | number | Estimated cost in USD |
current_spend | number | Current team spend |
budget | number | Team budget limit |
remaining_budget | number | Remaining budget |
Playground
POST /api/playground/run
Execute code in the playground. Requires authentication.
Rate limit: 10 requests/minute (shared with job submission).
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Source code (1-100,000 chars) |
language | string | Yes | auto, qiskit, cirq, pennylane, openqasm, or marqov |
backend | string | Yes | Backend slug |
shots | integer | Yes | Number of shots (1-100,000) |
Response (200):
| Field | Type | Description |
|---|---|---|
counts | object | Measurement outcome counts (e.g., {"00": 512, "11": 488}) |
backend | string | Backend used |
shots | integer | Shots executed |
execution_time_ms | number | Execution time in milliseconds |
cost_usd | number | Cost (currently always 0.0 for playground) |
language_detected | string | Resolved language |
circuit_info | object | Circuit metadata from worker |
Error codes: 400 (validation), 401 (unauthorized), 422 (language detection failed or worker error), 429 (rate limited), 502 (worker unreachable), 503 (worker not configured).
POST /api/playground/convert
Convert Python quantum code to OpenQASM without simulation. Requires authentication.
Rate limit: 10 requests/minute (shared with job submission).
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Python source code (1-100,000 chars) |
language | string | Yes | qiskit, cirq, pennylane, or marqov |
Response (200):
| Field | Type | Description |
|---|---|---|
qasm | string | OpenQASM output |
circuit_info | object | Circuit metadata |
Error codes: 400, 401, 422, 429, 502, 503.
POST /api/playground/telemetry
Record browser simulation telemetry events. Requires authentication.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | Yes | Client session identifier |
team_id | UUID | Yes | Team ID |
simulator | string | Yes | quantum-circuit or qulacs-wasm |
qasm_input | string | Yes | QASM input (max 100,000 chars) |
shots | integer | Yes | Shots (1-100,000) |
qubit_count | integer | Yes | Qubit count (1-50) |
gate_count | integer | Yes | Gate count (0-10,000) |
execution_time_ms | number | Yes | Client-side execution time |
counts | object | Yes | Measurement counts |
source_framework | string | No | qiskit, cirq, pennylane, or qasm |
user_agent | string | No | Browser user agent (max 500 chars) |
Response (201): { "success": true }
Backends
GET /api/backends
List all available quantum backends with pricing and metadata. Public endpoint — no authentication required.
Cache: CDN 5 min (s-maxage=300), client 10 min (max-age=600), stale-while-revalidate 1 hour.
Response (200):
| Field | Type | Description |
|---|---|---|
backends | Backend[] | Array of available backends |
updatedAt | string | ISO 8601 timestamp |
Backend object:
| Field | Type | Description |
|---|---|---|
id | UUID | Backend ID |
slug | string | URL-friendly identifier (e.g., sv1) |
name | string | Display name |
provider | string | Provider name (e.g., AWS Braket) |
deviceType | string | simulator or qpu |
providerTargetId | string | Provider device ARN or target ID |
region | string|null | AWS region |
qubitCount | integer|null | Number of qubits |
topology | string|null | Qubit connectivity topology |
isAvailable | boolean|null | Current availability |
status | string | online, offline, or maintenance |
pricing | object | { taskFee, perShot, minimumCost } |
maxShots | integer|null | Maximum shots per task |
maxQubits | integer|null | Maximum qubits |
description | string|null | Human-readable description |
documentationUrl | string|null | Link to provider docs |
tags | string[]|null | Searchable tags |
displayOrder | integer|null | Sort order |
isRecommended | boolean|null | Featured backend |
Scripts
GET /api/scripts
List scripts accessible to the current user. Requires authentication.
Query parameters:
| Param | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Max results (max 100) |
offset | integer | 0 | Pagination offset |
script_type | string | — | Filter by workflow or task |
user_only | boolean | false | Only show user’s own scripts |
Response (200):
| Field | Type | Description |
|---|---|---|
scripts | object[] | Script list (without script_content) |
total | integer | Total count for pagination |
limit | integer | Applied limit |
offset | integer | Applied offset |
Script list item:
| Field | Type | Description |
|---|---|---|
id | UUID | Script ID |
name | string | Script name |
description | string | Description |
script_type | string | workflow or task |
category | string | Script category |
is_public | boolean | Whether script is published |
is_hardcoded | boolean | Whether script is a platform template |
created_at | string | ISO 8601 timestamp |
executions_count | integer | Number of executions |
GET /api/scripts/:id
Get full details of a specific script, including content and recent executions. Requires authentication.
Response (200): Full script object plus executions array (last 10).
POST /api/scripts
Save a completed job’s inline code as a reusable script. Requires authentication.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
job_id | UUID | Yes | Completed job ID |
name | string | Yes | Script name (1-200 chars) |
description | string | Yes | Description (1-2,000 chars) |
slug | string | Yes | URL-friendly slug (lowercase, numbers, hyphens) |
Response (201): { "script_id": "uuid" }
Error codes: 400 (no inline code, not completed), 401, 403 (not owner), 404 (job not found), 409 (slug conflict).
POST /api/scripts/upload
Upload a new Python script with optional pip dependencies. Requires authentication.
Rate limit: 5 uploads/minute.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Script name (1-200 chars) |
description | string | No | Description (max 1,000 chars) |
script_content | string | Yes | Python source code |
requirements | string | No | requirements.txt content |
script_type | string | No | workflow or task (auto-detected if omitted) |
Response (200):
| Field | Type | Description |
|---|---|---|
script_id | UUID | Created script ID |
name | string | Script name |
script_type | string | Detected or specified type |
status | string | validated |
validation_warnings | string[] | Warnings from validation (if any) |
metadata | object | Script metadata from AST analysis |
PATCH /api/scripts/:id
Update a script’s metadata and/or code. Only for unpublished scripts owned by the user.
Request body (at least one field required):
| Field | Type | Description |
|---|---|---|
name | string | New name |
description | string | New description |
script_content | string | Updated Python source code (validated) |
requirements | string | Updated requirements.txt (validated) |
framework | string | Framework (qiskit, cirq, pennylane, openqasm, marqov) |
Error codes: 400 (validation failed, public script), 401, 403 (not owner), 404.
DELETE /api/scripts/:id
Soft-delete a script (sets is_active = false). Only for unpublished scripts owned by the user.
Error codes: 400 (public script), 401, 403 (not owner), 404.
Capsules
POST /api/capsules/create
Create a new capsule in draft state. Requires authentication.
Rate limit: 5 capsules/minute.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Capsule name (1-200 chars) |
description | string | No | Description (max 1,000 chars) |
tags | string[] | No | Up to 10 tags (max 50 chars each) |
workflow | object | No | Workflow definition with tasks array |
workflow.tasks[].name | string | Yes | Task name |
workflow.tasks[].script_id | UUID | Yes | Script ID |
workflow.tasks[].backend | string | Yes | Backend slug |
workflow.tasks[].parameters | object | Yes | Task parameters |
backends | string[] | No | Backend slugs |
max_budget_usd | number | No | Maximum budget (0-500) |
retention_years | integer | No | Data retention period (1-100) |
regulatory_framework | string | No | e.g., FDA-21CFR11 |
Response (201): { "capsule_id": "uuid" }
GET /api/capsules/:id
Fetch a single capsule by ID. Requires authentication (team-scoped via RLS).
PATCH /api/capsules/:id
Update a draft capsule. Only draft capsules can be modified. Owner only.
Request body: Same fields as create (all optional, at least one required).
Error codes: 400, 401, 403 (not draft or not owner), 404.
DELETE /api/capsules/:id
Delete a capsule. Cannot delete sealed or archived capsules. Owner only.
Error codes: 401, 403 (sealed/archived or not owner), 404.
POST /api/capsules/:id/seal
Seal a capsule, making it immutable.
POST /api/capsules/:id/run
Run a capsule in verify or template mode.
POST /api/capsules/:id/clone
Clone a capsule.
POST /api/capsules/:id/archive
Archive a sealed capsule.
POST /api/capsules/:id/publish
Publish a sealed capsule.
GET /api/capsules/:id/export
Export a capsule as a downloadable package.
POST /api/capsules/from-job
Create a capsule from an existing completed job.
POST /api/capsules/upload
Upload a capsule from a YAML definition file.
POST /api/capsules/import
Import a capsule package.
Team
GET /api/team/members
List all members of the authenticated user’s team. Requires authentication.
Query parameters:
| Param | Type | Default | Description |
|---|---|---|---|
team_id | UUID | User’s first team | Team to list members for |
Response (200):
| Field | Type | Description |
|---|---|---|
team_id | UUID | Team ID |
members | object[] | Array of member objects |
members[].user_id | UUID | Member user ID |
members[].email | string | Member email |
members[].name | string | Display name or email prefix |
members[].role | string | owner, admin, or member |
members[].joined_at | string | ISO 8601 timestamp |
PATCH /api/team/members/:userId
Update a team member’s role. Admin/owner only.
Rate limit: 30 requests/minute.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
team_id | string | Yes | Team ID |
role | string | Yes | admin or member (cannot assign owner) |
Error codes: 400, 401, 403 (not admin/owner, cannot change owner), 404, 429.
DELETE /api/team/members/:userId
Remove a member from the team. Admin/owner only. Cannot remove self or owner.
Rate limit: 30 requests/minute.
Request body: { "team_id": "uuid" }
GET /api/team/invitations
List pending invitations for the user’s team. Requires authentication.
Query parameters: team_id (optional).
POST /api/team/invitations
Create a team invitation. Admin/owner only.
Rate limit: 10 requests/minute.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
team_id | string | Yes | Team ID |
email | string | Yes | Invitee email |
role | string | Yes | admin or member |
message | string | No | Optional message (max 500 chars) |
Response (201): Invitation object with id, email, role, expires_at.
Error codes: 400 (already member, pending invite), 401, 403 (not admin/owner, suspended), 429.
DELETE /api/team/invitations/:id
Cancel a pending invitation.
GET /api/team/secrets
List all secrets for the user’s teams. Values are always masked ("value": "......"). Requires authentication.
POST /api/team/secrets
Create or update provider credentials. Admin/owner only.
Rate limit: 20 requests/minute.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
team_id | string | Yes | Team ID |
provider | string | Yes | Provider name (e.g., aws, azure) |
secrets | object | Yes | Key-value pairs of secret names and values |
Error codes: 400 (invalid provider, missing fields), 401, 403 (not admin/owner, suspended), 429.
DELETE /api/team/secrets/:id
Delete a specific secret. Admin/owner only.
POST /api/team/secrets/test
Test a secret connection (validate credentials).
GET /api/team/usage
Get team usage and spend data for the current or specified month. Requires authentication.
Query parameters: month (optional, format: YYYY-MM).
Response (200):
| Field | Type | Description |
|---|---|---|
period | object | { start, end, month } |
summary | object | { totalSpend, lastMonthSpend, changePercent } |
byProvider | object[] | Spend grouped by provider |
byBackend | object[] | Spend grouped by backend |
daily | object[] | Daily spend for charting |
Profile
PATCH /api/profile
Update user profile. Requires authentication.
Rate limit: 30 requests/minute.
Request body (all fields optional):
| Field | Type | Constraints |
|---|---|---|
username | string | 3-30 chars, alphanumeric + hyphens/underscores |
display_name | string|null | Max 100 chars |
bio | string|null | Max 500 chars |
avatar_url | string|null | Max 500 chars |
website | string|null | Max 200 chars |
location | string|null | Max 100 chars |
github_username | string|null | Max 39 chars |
twitter_username | string|null | Max 15 chars |
linkedin_url | string|null | Max 200 chars |
is_public | boolean | Profile visibility |
Error codes: 400 (username taken), 401, 429.
Rate Limits Summary
| Endpoint | Limit | Scope |
|---|---|---|
| POST /api/jobs/submit | 10/min | Per user |
| POST /api/jobs/submit | 100/day | Per team |
| POST /api/playground/run | 10/min | Per user (shared with job submit) |
| POST /api/playground/convert | 10/min | Per user (shared) |
| POST /api/capsules/create | 5/min | Per user |
| POST /api/capsules/import | 3/min | Per user |
| POST /api/scripts/upload | 5/min | Per user |
| POST /api/team/invitations | 10/min | Per user |
| POST /api/team/secrets | 20/min | Per user |
| PATCH /api/team/members/:id | 30/min | Per user |
| PATCH /api/profile | 30/min | Per user |
| POST /api/auth/validate-code | 5/min | Per IP |
| GET /api/* (proxy layer) | 100/min | Per user |
Rate limited responses return HTTP 429 with a Retry-After header (seconds until reset).
Common Error Responses
| Status | Meaning |
|---|---|
| 400 | Validation error (check error field for details) |
| 401 | Not authenticated |
| 402 | Budget exceeded (job submission only) |
| 403 | Forbidden (wrong role, suspended team, not owner) |
| 404 | Resource not found |
| 409 | Conflict (duplicate slug) |
| 422 | Unprocessable (language detection failed, worker execution error) |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
| 502 | Worker unreachable |
| 503 | Worker not configured |