Authenticated API docs

API keys, usage, quotas, and rejects

Dashboard-facing guidance for account owners and admins using the implemented API key, usage, entitlement, job, and file routes.

API key setup

Create API keys from a dashboard owner/admin session. The platform returns plaintext_key one time, then list and revoke responses expose metadata only. Never log, persist in job payloads, or screenshot real key values.

curl https://api.webot.agency/api/v1/api-keys \
  -X POST \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Fulfillment client" }'

{
  "api_key": {
    "id": "key_example",
    "name": "Fulfillment client",
    "prefix": "a1b2c3d4e5f6",
    "scopes": ["api:read", "api:write"],
    "status": "active"
  },
  "plaintext_key": "wbot_a1b2c3d4e5f6_exampleTokenForOneTimeDisplay"
}

Read account access

Current account

curl https://api.webot.agency/api/v1/me \
  -H "Authorization: Bearer $WEBOT_API_KEY"

Entitlements

curl https://api.webot.agency/api/v1/entitlements \
  -H "Authorization: Bearer $WEBOT_API_KEY"

Usage

curl "https://api.webot.agency/api/v1/usage?job_id=job_example" \
  -H "Authorization: Bearer $WEBOT_API_KEY"

Usage and quota interpretation

Usage is server-written. Clients can read current-period totals, remaining quota, service-family buckets, entitlement buckets, billing-period buckets, API-key buckets, and recent safe usage events.

Remaining capacity is calculated from verified active plans created by signed Stripe webhooks. Display-only checkout return parameters do not change quota.

{
  "usage": {
    "period": {
      "start": "2026-06-01T00:00:00.000Z",
      "end": "2026-07-01T00:00:00.000Z"
    },
    "totals": {
      "quantity": 12,
      "quota": 500,
      "remaining": 488,
      "unit": "job"
    },
    "summaries": {
      "by_entitlement": [
        { "key": "api_agent_top", "quantity": 12, "quota": 500, "remaining": 488 }
      ],
      "by_service_family": [
        { "key": "create_polish", "quantity": 7 }
      ],
      "by_api_key": [
        { "api_key_id": "key_example", "quantity": 12 }
      ]
    },
    "recent_events": []
  }
}

File presign

Presign requests create short-lived upload contracts for account-owned work. They do not create durable public file URLs.

curl https://api.webot.agency/api/v1/files/presign \
  -X POST \
  -H "Authorization: Bearer $WEBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "purpose": "job_source",
    "content_type": "application/pdf",
    "size_bytes": 1048576,
    "file_name": "source-notes.pdf",
    "job_id": "job_example",
    "retention_days": 7
  }'

Reject and error codes

Reject responses are designed for customer and agent decisions. Use the code and action; do not parse message text.

{
  "error": {
    "code": "quota_exceeded",
    "message": "This account has reached its current plan limit.",
    "action": "Wait for the next billing period or choose a plan with more capacity.",
    "request_id": "req_example"
  }
}

Rate-limit rejects also include a standard Retry-After header. The JSON body mirrors it as retry_after_seconds and includes retry_at so agents can schedule a retry without parsing message text.

HTTP/1.1 429 Too Many Requests
Retry-After: 60

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "This request is over the current rate limit.",
    "action": "Wait briefly before trying again, or reduce request frequency.",
    "request_id": "req_example",
    "retry_after_seconds": 60,
    "retry_at": "2026-06-28T14:01:00.000Z"
  }
}
  • invalid_request: request body or parameter shape is not usable.
  • unsupported_service: service family/type is not in the catalog.
  • entitlement_required: account does not have active access for the request.
  • quota_exceeded: account has no remaining quota for the current period.
  • rate_limit_exceeded: caller exceeded the current route/key/account limit; use Retry-After or retry_after_seconds before retrying.
  • concurrency_limit_exceeded: account already has the maximum active jobs for its tier.
  • job_not_found: job is missing or not owned by the account.
  • job_not_cancelable: job has reached a status that cannot be canceled.
  • revision_unavailable: revision is not available for the job.
  • unsupported_file: upload content type or purpose is not supported.
  • file_too_large: file exceeds the current upload limit.
  • unsafe_request: request cannot be accepted safely.