REST API

Authentication

Base URL, API keys, scopes, auth headers, auth errors and rate limits.

Read as Markdown

Everything you need to make an authenticated call to the REST API.

The two lines you need
Base URL   https://app.riffads.com/api/v1
Auth       Authorization: Bearer sk_live_...
  • One version: v1. No version header.
  • riffads.com is the marketing site. It does not serve /api/v1.
  • MCP clients use OAuth, not keys. See MCP connect.

Server side only

No CORS headers, no OPTIONS handler. The key is a secret. Call from a backend, task runner, CI job or terminal. Never a browser.

Send the key

Terminal
export RIFFADS_API_KEY="sk_live_..."

curl -s https://app.riffads.com/api/v1/capabilities \
  -H "Authorization: Bearer $RIFFADS_API_KEY"

Header rules

x-api-key: sk_live_... works anywhere Authorization does.

CaseResult
Both headers sentA usable Authorization: Bearer wins. A non-Bearer Authorization counts as absent, so x-api-key is read
Scheme casingCase-insensitive: bearer, Bearer, BEARER
Extra whitespaceAllowed between scheme and key
Non-Bearer scheme (Basic abc), no x-api-keyCounts as no header. Plain 401
Key in a query paramNot supported. 401

Missing header, malformed header and unknown key all get the same 401, byte for byte.

Get a key

Owners and admins create keys at app.riffads.com/api-keys. No API endpoint creates, rotates or lists keys.

  • The workspace plan must include the API. Every paid plan does.
  • Prefix is sk_live_ everywhere. No sk_test_, no sandbox. Every call is real.
  • Shown once. Stored as a hash. Lost it? Create a new one.
  • A key belongs to the workspace, not the person who made it. It only reaches its own workspace.
  • Keys never expire. They work until revoked.
  • Name is required, up to 60 characters. Name it after where it lives (CI pipeline, staging).
  • A key can carry an optional 24 hour spend cap, set in the app.

Scopes

Pick scopes when you create the key. They can never be widened. Need another scope? Create a new key.

ScopeOpens
ReadOn every key. Capabilities, actors, voices, balance, estimates, all generation, batch and workflow reads. Spends nothing
GenerateRead, plus uploads and POST /generations
WorkflowsRead, plus both workflow invoke routes

A Generate key can't invoke a workflow. A Workflows key can't submit a generation. Need both? Tick both.

EndpointScope
GET /capabilities, GET /capabilities/{id}Read
POST /estimatesRead
GET /actors, GET /voicesRead
GET /generations/{id}, GET /generations/{id}/waitRead
GET /batches/{id}Read
GET /workflows/templates, GET /workflow-runs/{id}Read
POST /generationsGenerate
POST /uploads, POST /uploads/{assetId}/finalizeGenerate
POST /workflows/{id}/invokeWorkflows
POST /workflows/templates/{key}/invokeWorkflows

A read-only key can't spend. Safe for dashboards and reports.

Revoke and rotate

  • Revoke in the app. It takes effect on the next call.
  • A revoked key answers 401 not_authorized.
  • Work already running finishes.
  • To rotate: create the new key, deploy it (both work meanwhile), then revoke the old one.
  • No cap on keys per workspace. One key per integration keeps limits and revocation separate.
  • Committed a key to a repo? Revoke it now.

Auth errors

Every failure is wrapped in error. Every 401 sends WWW-Authenticate: Bearer realm="RiffAds".

Response: 401
{
  "error": {
    "ok": false,
    "code": "not_authorized",
    "message": "This RiffAds API key is not valid. Check the Authorization header, or create a new key at riffads.com.",
    "retryable": false,
    "credits_charged": 0
  }
}
CausecodeHTTPRetry
No header, non-Bearer scheme, blank or unknown keynot_authorized401no
Key revokednot_authorized401no
Key expired (only very old keys)not_authorized401no
Key's request allowance used upquota_exceeded429no
Too many requestsrate_limited429yes
Key lacks the scope this endpoint needsinsufficient_scope403no
Workspace deletedworkspace_unavailable410no
Plan does not include the APIrequired_plan403no
Read-only connection tried to spendread_only_connection403no
Key check failed on our sideinternal_error500yes

When several things are wrong, you get them in this order: header, key (revoked, allowance, rate limit, scope), workspace, plan.

Messages say riffads.com

Error messages say "create a new key at riffads.com". Keys are made at app.riffads.com/api-keys.

Rate limits

Two brakes. Either can return 429 rate_limited.

Per key: 120 requests a minute. The number is copied onto the key when it is created.

Per action: fixed one minute windows, counted per key. Two keys never share a bucket.

BucketPer minuteEndpoints
Reads120Capabilities, actors, voices, balance, generation and batch reads, wait, templates, workflow runs
Estimates60POST /estimates
Submits20POST /generations
Uploads30POST /uploads and finalize
Workflow runs20Both invoke routes
  • One wait call costs 1 read, however long it blocks.
  • No X-RateLimit-* headers.
  • A 429 sends Retry-After only when the body has retry_after_seconds. Same number.
  • quota_exceeded is a 429 you can't retry and sends no Retry-After.

All numbers: limits. Retry rules: conventions.

On this page