Consent Checker API
Build against the same consent-testing engine that powers the web app and MCP server.
The API is intentionally simple:
- one API key belongs to one subscribed account
- every run created with that key belongs to that account
- API-created runs still appear in the web app GUI
- the live browser session still opens in the web app
- completed reports still show up in My Tests and the Results page
What You Get
- Start a consent check run
- Poll run status
- List your recent runs
- Fetch the markdown report
- Fetch the raw run session JSON
- Fetch screenshot metadata and browser-usable image URLs
- Fetch the detailed tracking markdown artifact used by AI summaries
- Start background generation for the same assessment-style analysis shown in the Results page
- Start background generation for got cipa? analysis on California runs
- Poll the current analysis status and fetch the generated summary when ready
Base URL:
https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1
Authentication header:
Authorization: Bearer <api_key>
1. How To Get An API Key
- Log in to the web app.
- Make sure your account has an active subscription.
- Open
Account. - In the
API Accesssection, clickCreate API Key. - Copy the key immediately.
Important:
- the plaintext key is shown only once
- existing keys can be listed by prefix and revoked later
- if you revoke a key, any integration using it stops working immediately
2. Quickstart
Set your key:
export CONSENT_API_KEY='your_api_key_here'
Create a run:
curl -X POST https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs \
-H "Authorization: Bearer $CONSENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"task": "reject_all",
"state": "US-CA",
"wait_for_debug_url_seconds": 3
}'
Example success response:
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"task_id": "4ab5e1df-3b12-4d17-a8b4-98b8d4f8a0f6",
"status": "started",
"message": "Analysis started successfully",
"debug_url": "https://www.browserbase.com/sessions/...",
"links": {
"status": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"report": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/report",
"session": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/session",
"analysis": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/analysis",
"live_view": "/live/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"results": "/results/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68"
}
}
What good looks like:
- you get back a
session_id statusisstartedlinks.live_viewopens the same live session in the web app- once complete, the run appears in the GUI under
My Tests
3. Endpoint Reference
Quick scan:
| Method | Path | What it does |
|---|---|---|
GET |
/api/v1 |
Returns a lightweight API index. |
POST |
/api/v1/runs |
Starts a new consent check run. |
GET |
/api/v1/runs |
Lists recent persisted runs for the API key owner. |
GET |
/api/v1/runs/<session_id> |
Returns the latest status for one run. |
DELETE |
/api/v1/runs/<session_id> |
Deletes a persisted run and its stored artifacts. |
GET |
/api/v1/runs/<session_id>/report |
Returns the completed markdown report. |
GET |
/api/v1/runs/<session_id>/session |
Returns the raw session JSON for a completed run. |
GET |
/api/v1/runs/<session_id>/screenshots |
Returns screenshot metadata and browser-usable URLs. |
GET |
/api/v1/runs/<session_id>/tracking-data |
Returns the detailed tracking markdown artifact used by summaries. |
GET |
/api/v1/runs/<session_id>/analysis |
Returns the current default analysis state and summary, if available. |
POST |
/api/v1/runs/<session_id>/analysis |
Starts default analysis generation in the background. |
GET |
/api/v1/runs/<session_id>/cipa-analysis |
Returns the current got cipa? analysis state for California runs. |
POST |
/api/v1/runs/<session_id>/cipa-analysis |
Starts got cipa? analysis generation in the background for California runs. |
Shared conventions:
- analysis-style endpoints may return a JSON body with
statusvalues such asnot_ready,not_generated,pending,running,completed,failed, orreport_not_available POSTanalysis endpoints return202 Acceptedwhen work is queued or in progress, and200 OKwhen a cached or completed summary is returned immediatelyGET /cipa-analysisandPOST /cipa-analysisreturn400 Bad Requestfor non-California runs- summary endpoints return HTML in
summary - summary retrieval is language-aware: if the request is made with the app's French language context, the API may return a French translated summary; otherwise English is the default
GET /api/v1
What it does:
- returns a lightweight API index
- useful for checking that the API is reachable
Example:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1 \
-H "Authorization: Bearer $CONSENT_API_KEY"
Example response:
{
"name": "Consent Checker API",
"version": "v1",
"auth": "Bearer API key",
"endpoints": {
"create_run": "/api/v1/runs",
"get_run_status": "/api/v1/runs/<session_id>",
"delete_run": "/api/v1/runs/<session_id>",
"list_runs": "/api/v1/runs",
"get_run_report": "/api/v1/runs/<session_id>/report",
"get_run_session": "/api/v1/runs/<session_id>/session",
"get_run_screenshots": "/api/v1/runs/<session_id>/screenshots",
"get_run_tracking_data": "/api/v1/runs/<session_id>/tracking-data",
"analyze_run": "/api/v1/runs/<session_id>/analysis",
"analyze_run_cipa": "/api/v1/runs/<session_id>/cipa-analysis"
}
}
POST /api/v1/runs
What it does:
- starts a new consent check run for the account that owns the API key
Input format:
{
"url": "https://example.com",
"task": "reject_all",
"state": "US-CA",
"wait_for_debug_url_seconds": 3
}
Field notes:
url: required, target websitetask: optional, defaults toreject_alltasksupportsaccept_all,reject_all,gpc,granular_preferencesstate: optional, defaults toUS-CAgranular_preference_prompt: advanced optional field, required only whentaskisgranular_preferenceswait_for_debug_url_seconds: optional, lets the API wait briefly for the live debug URL
No new create-run parameter is required for got cipa? analysis. Create the run with state set to US-CA or CA, then call the separate /cipa-analysis endpoint after the run completes. Existing POST /api/v1/runs workflows continue to work unchanged.
This API behavior is separate from the web UI's mode persistence. The web UI can still remember and pass its selected mode across pages; the public create-run API simply does not require a new field for existing integrations.
Output format:
{
"session_id": "uuid",
"task_id": "worker-task-id",
"status": "started",
"message": "Analysis started successfully",
"debug_url": "https://...",
"links": {
"status": "/api/v1/runs/<session_id>",
"report": "/api/v1/runs/<session_id>/report",
"session": "/api/v1/runs/<session_id>/session",
"screenshots": "/api/v1/runs/<session_id>/screenshots",
"analysis": "/api/v1/runs/<session_id>/analysis",
"cipa_analysis": "/api/v1/runs/<session_id>/cipa-analysis",
"tracking_data": "/api/v1/runs/<session_id>/tracking-data",
"live_view": "/live/<session_id>",
"results": "/results/<session_id>"
}
}
Good example:
curl -X POST https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs \
-H "Authorization: Bearer $CONSENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://www.nytimes.com",
"task": "gpc",
"state": "US-CA"
}'
GET /api/v1/runs/<session_id>
What it does:
- returns the latest status for one run
- only works if the run belongs to the API key owner
Example:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id> \
-H "Authorization: Bearer $CONSENT_API_KEY"
Example response while running:
{
"url": "https://example.com",
"task": "reject_all",
"status": "running",
"progress": 35,
"message": "Processing...",
"links": {
"status": "/api/v1/runs/<session_id>",
"report": "/api/v1/runs/<session_id>/report",
"session": "/api/v1/runs/<session_id>/session",
"screenshots": "/api/v1/runs/<session_id>/screenshots",
"analysis": "/api/v1/runs/<session_id>/analysis",
"cipa_analysis": "/api/v1/runs/<session_id>/cipa-analysis",
"tracking_data": "/api/v1/runs/<session_id>/tracking-data",
"live_view": "/live/<session_id>",
"results": "/results/<session_id>"
}
}
Example response when complete:
{
"url": "https://example.com",
"task": "reject_all",
"status": "completed",
"progress": 100,
"debug_url": "https://www.browserbase.com/sessions/...",
"links": {
"status": "/api/v1/runs/<session_id>",
"report": "/api/v1/runs/<session_id>/report",
"session": "/api/v1/runs/<session_id>/session",
"screenshots": "/api/v1/runs/<session_id>/screenshots",
"live_view": "/live/<session_id>"
}
}
What good looks like:
statuseventually becomescompleted- you can open
links.live_viewin the web app - the run later appears in the GUI under
My Tests
DELETE /api/v1/runs/<session_id>
What it does:
- permanently deletes a persisted run owned by the API key account
- removes stored report/session/screenshot artifacts before deleting the database record
- does not cancel an in-progress run
Example:
curl -X DELETE https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id> \
-H "Authorization: Bearer $CONSENT_API_KEY"
Example response:
{
"success": true,
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"deleted": {
"test_run": true,
"storage_objects": 4,
"storage_paths": [
"user_example_com/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/example_reject_all_US_CA_20260430-120000_tracker_analysis.md",
"user_example_com/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/example_reject_all_US_CA_20260430-120000_session.json",
"user_example_com/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/screenshot.png",
"user_example_com/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/example_reject_all_US_CA_20260430-120000_compliance_summary.html"
],
"redis_keys": 1,
"local_files": 2
}
}
GET /api/v1/runs
What it does:
- lists the recent persisted runs for the API key owner
Example:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs \
-H "Authorization: Bearer $CONSENT_API_KEY"
Example response:
[
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"user_email": "user@example.com",
"website": "https://example.com",
"flow": "reject_all",
"state_code": "US-CA",
"created_at": "2026-03-19T15:40:12.000000",
"md_path": "user_example_com/9c33.../example_com_reject_all_US_CA_20260319-154012_tracker_analysis.md",
"json_path": "user_example_com/9c33.../example_com_reject_all_US_CA_20260319-154012_session.json",
"bucket": "runs",
"links": {
"status": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"report": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/report",
"session": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/session",
"screenshots": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/screenshots",
"analysis": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/analysis",
"live_view": "/live/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"results": "/results/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68"
}
}
]
GET /api/v1/runs/<session_id>/screenshots
What it does:
- returns screenshot metadata for a completed run
- includes browser-usable image URLs that can be rendered directly in HTML
- only works if the run belongs to the API key owner
Example:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id>/screenshots \
-H "Authorization: Bearer $CONSENT_API_KEY"
Example response:
[
{
"supabase_path": "user/<session_id>/cookie-banner.png",
"url": "https://...",
"name": "cookie-banner.png",
"label": "Initial Privacy Interface",
"type": "cookie_banner"
}
]
What good looks like:
- each item includes a
url - the returned
urlcan be used directly in an<img src="..."> - the client does not need a Supabase API key if the URL is already public or pre-signed
GET /api/v1/runs/<session_id>/tracking-data
What it does:
- returns the detailed tracking markdown artifact for a completed run
- exposes the request-level evidence now collected for AI summaries, including grouped tracker inventory, interpreted fields, identifiers, consent-signal clues, first-party relay notes, cookies, screenshots, and remediation hints
- only works if the run belongs to the API key owner
- returns
status: "not_ready"if the run has not finished yet - returns
404if no detailed tracking artifact can be found for the completed run
Example:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id>/tracking-data \
-H "Authorization: Bearer $CONSENT_API_KEY"
Example response:
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"status": "completed",
"content_type": "text/markdown",
"detailed_tracking": "# Detailed Tracking Evidence\n\n...",
"source_path": "user_example_com/..._detailed_tracking.md",
"source_url": "https://...",
"session_artifact": {
"detailed_tracking_path": "logs/<session_id>_detailed_tracking.md",
"detailed_tracking_storage_path": "user_example_com/..._detailed_tracking.md"
},
"links": {
"status": "/api/v1/runs/<session_id>",
"report": "/api/v1/runs/<session_id>/report",
"session": "/api/v1/runs/<session_id>/session",
"screenshots": "/api/v1/runs/<session_id>/screenshots",
"analysis": "/api/v1/runs/<session_id>/analysis",
"cipa_analysis": "/api/v1/runs/<session_id>/cipa-analysis"
}
}
What good looks like:
content_typeistext/markdowndetailed_trackingcontains the full detailed evidence artifactsource_pathidentifies the storage or local artifact usedsource_urlis present when the server can resolve a browser-usable storage URL
Example not-ready response:
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"status": "not_ready",
"message": "Run must be completed before detailed tracking data can be returned",
"links": {
"status": "/api/v1/runs/<session_id>",
"tracking_data": "/api/v1/runs/<session_id>/tracking-data"
}
}
GET /api/v1/runs/<session_id>/report
What it does:
- returns the completed markdown report as
text/markdown
Example:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id>/report \
-H "Authorization: Bearer $CONSENT_API_KEY"
Output format:
- plain markdown text
- same underlying report content you can read in the GUI
What good looks like:
- the report endpoint returns markdown
- the same run can also be opened from the web app Results page
GET /api/v1/runs/<session_id>/session
What it does:
- returns the raw session JSON for a completed run
Example:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id>/session \
-H "Authorization: Bearer $CONSENT_API_KEY"
Example response:
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"events": [],
"screenshots": []
}
GET /api/v1/runs/<session_id>/analysis
What it does:
- returns the current analysis state for a completed run
- includes the generated summary HTML once it is available
- does not start generation by itself
- may return
status: "not_ready"while the run itself is still in progress - may return
status: "report_not_available"if the run finished without a readable report artifact - summary output follows the request language context used by the app, with English as the default
Example:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id>/analysis \
-H "Authorization: Bearer $CONSENT_API_KEY"
Example response:
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"status": "completed",
"summary": "<h3>Applicable Laws & Enforcement</h3><p>...</p><h3>Assessment</h3><ul><li>...</li></ul>",
"links": {
"status": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"report": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/report",
"session": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/session",
"analysis": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/analysis",
"live_view": "/live/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"results": "/results/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68"
}
}
What good looks like:
statusmoves through states likenot_ready,report_not_available,not_generated,pending,running,completed, orfailedsummaryisnulluntil generation finisheslinks.resultsopens the same run in the web app
Example not-ready response:
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"status": "not_ready",
"message": "Run must be completed before analysis can be generated",
"assessment_mode": "default",
"links": {
"status": "/api/v1/runs/<session_id>",
"analysis": "/api/v1/runs/<session_id>/analysis"
}
}
POST /api/v1/runs/<session_id>/analysis
What it does:
- starts background generation for the run analysis
- returns immediately with a task ID instead of waiting for the summary
- can force regeneration even if a cached summary already exists
- accepts a JSON boolean for
force_regenerate - may return
200 OKwithstatus: "completed"if a cached summary already exists - may return
status: "not_ready"orstatus: "report_not_available"if generation cannot start yet
Example:
curl -X POST https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id>/analysis \
-H "Authorization: Bearer $CONSENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"force_regenerate": false
}'
Example queued response:
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"task_id": "8d1ff6f4-5f86-4faf-bf3a-bbc8f0ea1111",
"status": "queued",
"message": "Summary generation started in background",
"summary": null,
"links": {
"status": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"report": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/report",
"session": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/session",
"analysis": "/api/v1/runs/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68/analysis",
"live_view": "/live/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"results": "/results/9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68"
}
}
What good looks like:
- the request returns quickly with
status: "queued"or an in-progress status - you poll
GET /api/v1/runs/<session_id>/analysisuntil it returnscompleted - use
"force_regenerate": trueto rebuild the summary from scratch
GET /api/v1/runs/<session_id>/cipa-analysis
What it does:
- returns the current got cipa? analysis state for a completed California run
- uses the CIPA-specific summary mode, separate from the default compliance summary cache
- does not start generation by itself
- returns
400if the run was not performed from California (US-CAorCA) - may return
status: "not_ready"while the run itself is still in progress - may return
status: "report_not_available"if the run finished without a readable report artifact - summary output follows the request language context used by the app, with English as the default
Example:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id>/cipa-analysis \
-H "Authorization: Bearer $CONSENT_API_KEY"
Example response:
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"status": "completed",
"assessment_mode": "cipa",
"summary": "<h3>CIPA Wiretap Matrix</h3><p>...</p>",
"links": {
"analysis": "/api/v1/runs/<session_id>/analysis",
"cipa_analysis": "/api/v1/runs/<session_id>/cipa-analysis",
"tracking_data": "/api/v1/runs/<session_id>/tracking-data"
}
}
What good looks like:
assessment_modeiscipastatusmoves through states likenot_ready,report_not_available,not_generated,pending,running,completed, orfailed- use this endpoint only for California runs; non-California runs receive
400
POST /api/v1/runs/<session_id>/cipa-analysis
What it does:
- starts background generation for got cipa? CIPA-focused analysis
- is only available for California runs (
stateset toUS-CAorCAwhen the run was created) - does not require or use an
assessment_modefield onPOST /api/v1/runs - can force regeneration even if a cached CIPA summary already exists
- accepts a JSON boolean for
force_regenerate - may return
200 OKwithstatus: "completed"if a cached CIPA summary already exists - may return
status: "not_ready"orstatus: "report_not_available"if generation cannot start yet
Example:
curl -X POST https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/<session_id>/cipa-analysis \
-H "Authorization: Bearer $CONSENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"force_regenerate": false
}'
Example queued response:
{
"session_id": "9c33bd5a-c3a5-467e-b5b4-4e8f5ddcfa68",
"task_id": "8d1ff6f4-5f86-4faf-bf3a-bbc8f0ea1111",
"status": "queued",
"assessment_mode": "cipa",
"message": "Summary generation started in background",
"summary": null
}
What good looks like:
- the request returns
202 Acceptedwhen generation is queued or already in progress - poll
GET /api/v1/runs/<session_id>/cipa-analysisuntilstatusbecomescompleted - non-California runs receive
400 Bad Request
4. End-To-End Example
Create a run, then poll it:
SESSION_ID=$(curl -s -X POST https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs \
-H "Authorization: Bearer $CONSENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"task": "reject_all",
"state": "US-CA"
}' | python3 -c "import sys,json; print(json.load(sys.stdin)['session_id'])")
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/$SESSION_ID \
-H "Authorization: Bearer $CONSENT_API_KEY"
Then fetch the report:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/$SESSION_ID/report \
-H "Authorization: Bearer $CONSENT_API_KEY"
Then generate the assessment:
curl -X POST https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/$SESSION_ID/analysis \
-H "Authorization: Bearer $CONSENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Then poll for it:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/$SESSION_ID/analysis \
-H "Authorization: Bearer $CONSENT_API_KEY"
For California runs, generate and poll the got cipa? analysis separately:
curl -X POST https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/$SESSION_ID/cipa-analysis \
-H "Authorization: Bearer $CONSENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/$SESSION_ID/cipa-analysis \
-H "Authorization: Bearer $CONSENT_API_KEY"
Fetch the detailed tracking evidence used by summaries:
curl https://papaya-consent-check-be11a0846ed5.herokuapp.com/api/v1/runs/$SESSION_ID/tracking-data \
-H "Authorization: Bearer $CONSENT_API_KEY"
5. GUI + Live View
API usage is not separate from the product experience.
If you start a run through the API:
- the returned
links.live_viewopens the live browser session in the web app - the finished report shows up in your GUI
- the run shows up under
My Tests - the report is still available through the Results page
That means developers can automate runs while still using the GUI for investigation and sharing.
6. Errors
Common status codes:
401 Unauthorized: missing or invalid API key403 Forbidden: inactive subscription for API access404 Not Found: run not found or not owned by the API key owner422or400: invalid input payload500: server error