Authentication

API key format, scopes, brand restriction, and key rotation.

Every request to the Public API authenticates with an opaque bearer token in the Authorization header. Browser CORS calls are blocked at the edge, so the API is server-to-server only.

The header

curl https://api.rankprompt.com/v1/me \
-H "Authorization: Bearer rp_live_YOUR_KEY"
const res = await fetch('https://api.rankprompt.com/v1/me', {
headers: { Authorization: `Bearer ${process.env.RANKPROMPT_API_KEY}` },
});
const me = await res.json();
import os, httpx

res = httpx.get(
  "https://api.rankprompt.com/v1/me",
  headers={"Authorization": f"Bearer {os.environ['RANKPROMPT_API_KEY']}"},
  timeout=30,
)
res.raise_for_status()
me = res.json()

Keys are prefixed with rp_live_ so they are easy to detect in logs and pull requests. We submit the prefix to GitHub secret scanning so leaks are caught automatically.

Scopes

Scopes are granular per resource. The implication hierarchy is: *read:all / write:all ⊃ specific scopes; write:fooread:foo.

ScopeGrants
read:all Read access to every resource type.
write:all Read + write access to every resource type.
read:brands List and inspect brands.
write:brands Create, update, and delete brands (implies read).
read:reports List and inspect reports.
write:reports Create, update, and delete reports (implies read).
write:prompts Create, update, and delete prompts (implies read).
read:jobs List and inspect jobs.
write:jobs Create, update, and delete jobs (implies read).
read:region-configs List and inspect region-configs.
write:region-configs Create, update, and delete region-configs (implies read).
read:scheduled List and inspect scheduled.
write:scheduled Create, update, and delete scheduled (implies read).
read:white-label List and inspect white-label.
write:white-label Create, update, and delete white-label (implies read).
read:citations List and inspect citations.
read:meta List and inspect meta.

read:meta and GET /v1/me

GET /v1/me is the introspection endpoint that confirms your credentials work. It always returns user_id, auth_type and plan. The fields that describe what your key can do (key_prefix, scopes, allowed_brand_id) are only included when the key carries the read:meta scope.

When a request lands with insufficient scope you get back the insufficient_scope error from the error catalog: a 403 whose details carry both the missing scope (required_scope) and the scopes your key actually has (key_scopes):

{
  "error": {
    "code": "insufficient_scope",
    "message": "This endpoint requires the 'write:brands' scope",
    "details": {
      "required_scope": "write:brands",
      "key_scopes": ["read:brands"]
    }
  },
  "request_id": "01HZK3..."
}

Brand restriction

Optionally bind a key to a single brand at creation time (allowed_brand_id). The API will then refuse any request whose target brand does not match, even if the calling key carries write:all. This lets you issue per-brand keys to clients without having to spin up a new Rank Prompt workspace.

A request that crosses the boundary fails with brand_not_authorized (403).

Lifecycle: create, rotate, revoke

Manage keys from the Developers page inside your workspace. Common operations:

  • Create: pick scopes, optional brand restriction, and optional expiry. The full key is shown once at creation time and never again.
  • Rotate: create a new key, deploy it, then revoke the old one. There is no “regenerate” button on purpose. Explicit rotation makes overlapping keys the default and prevents downtime.
  • Revoke: disables the key immediately. In-flight requests still complete; the next call returns invalid_api_key.
  • Expire: keys with an expires_at start returning expired_api_key once the timestamp passes. Use this for short-lived integrations.

Storage hygiene

  • Treat keys like database passwords. Never commit them, never log them, never put them in client-side code.
  • Prefer your platform’s secret manager (AWS Secrets Manager, GCP Secret Manager, Vercel/Netlify env vars, Doppler, 1Password CLI).
  • Rotate immediately on suspected exposure; we cannot revoke retroactively what has already been read from your codebase.