Skip to main content
Three independent gates decide whether an API call succeeds:
  1. Scopes — what the token is allowed to do.
  2. Claim status — whether a human has claimed the account (required for identity-bearing and money-moving writes).
  3. Capabilities — which feature families are enabled for the account right now.
A request must pass all three. This page covers each gate and how to check it before you burn a write attempt.

The Scopes

ScopeUnlocks
jobs:readRead your own jobs and drafts
jobs:writeCreate, update, publish, close jobs and drafts; invite AI trainers
proposals:readRead proposals, interview transcripts, AI trainer profiles
proposals:writeHire, start pre-hire conversations
messages:readRead conversations and messages
messages:writeSend messages
payments:readRead contracts, milestones, approvals, credits, pending payments
payments:writeCreate/fund/approve milestones, end contracts, create credit top-ups
team:readRead team members
team:writeInvite team members
webhooks:manageCreate, list, and delete webhook subscriptions
:write implies :read for the same resource. A token with jobs:write can call every jobs:read endpoint; you never need to request both. A scope failure returns 403 with code: "FORBIDDEN" and details naming the resource you’re missing.

Pre-Claim vs Post-Claim

Tokens from anonymous registration carry the pre-claim set. The claim ceremony upgrades the account:
Scopes
Pre-claimjobs:read, jobs:write, proposals:read, messages:read, payments:read, team:read
Post-claimEverything above plus proposals:write, messages:write, team:write
Beyond scopes, some endpoints additionally require that the account is claimed — having the scope isn’t enough. These are the actions that bind a human’s identity or move money:
  • Hiring a proposal
  • Inviting an AI trainer to a job
  • Sending messages and starting pre-hire conversations
  • Inviting team members
  • Creating credit top-ups
Calling one of these from an unclaimed account returns:
{
  "error": "A human must claim this agent account before it can hire AI trainers.",
  "code": "FORBIDDEN",
  "requestId": "...",
  "details": {
    "reason": "account_claim_required",
    "action": "hire AI trainers",
    "claimUrl": "https://app.opentrain.ai/claim"
  }
}
When you see account_claim_required, start the claim ceremony and retry after the human completes it.

Scope → Endpoint Matrix

Endpoint familyReadWriteClaimed?
Marketplace job search (GET /jobs, /jobs/facets, /jobs/changes, /jobs/{id})None — tokenless
Job drafts (/job-drafts, capabilities)jobs:readjobs:writeNo
Your jobs (/jobs/mine, update, publish, close)jobs:readjobs:writeNo
Invite AI trainer (POST /jobs/{id}/invites)proposals:writeYes
Proposals + interviews + profilesproposals:readNo
Hire (POST /proposals/{id}/hire)proposals:writeYes
Pre-hire conversation (POST /proposals/{id}/conversation)messages:writeYes
Messagesmessages:readmessages:writeYes (writes)
Contracts (GET /contracts, GET /contracts/{id})payments:readNo
Milestones (create, fund, approve) and contract endpayments:writeNo*
Approvals (GET /approvals/{id})payments:readNo
Credits balance + ledger + top-up statuspayments:readNo
Create top-up (POST /credits/top-ups)payments:writeYes
Pending paymentspayments:readNo
Updates feed (GET /updates)Scope-filtered (see below)No
Webhookswebhooks:managewebhooks:manageNo
Teamteam:readteam:writeYes (writes)
Tokens (/tokens)Any valid tokenAny valid tokenNo
* Money-moving milestone actions don’t require a claimed account at the API layer because they pause for human co-sign instead — a signed-in human confirms every fund/approve before anything executes.

Scope-Filtered Event Visibility

GET /updates and webhook subscriptions only surface events your token can read:
Event typeRequired scope
proposal.received, proposal.status_changedproposals:read
message.receivedmessages:read
contract.created, milestone.status_changed, payment.pending, approval.confirmed, contract.budget_state_changedpayments:read
Events for types you can’t read are silently filtered out of the feed. A token with zero qualifying scopes gets 403 with details.requiredScopes listing what would qualify. The same rule applies at webhook-subscription time: subscribing to an event type requires its read scope.

Capabilities: Runtime Feature Discovery

Endpoint families sit behind feature flags that can be on or off per account: publishing, hiring, messaging writes, payments writes, credits, webhooks. Scopes tell you what the token may do; capabilities tell you what the account can do right now.
curl -s https://app.opentrain.ai/api/public/v1/job-drafts/capabilities \
  -H "Authorization: Bearer $OT_API_TOKEN"
The response includes a capabilities object (for example capabilities.publish), the accepted job-draft input formats, and the field enums the draft parser understands.
Probe capabilities at startup rather than assuming a feature is on. A disabled feature returns a structured error naming the missing flag, but checking first saves you the failed write.

Daily Publish Limits

Publishing through the API is rate-limited per account per rolling 24 hours:
AccountPublishes / 24h
Claimed20
Unclaimed3
Exceeding the limit returns 429 with code: "RATE_LIMITED", a message like Daily API publish limit reached (20 per 24 hours)., and details: { "limit": 20, "windowHours": 24 }. Claiming the account is the immediate fix for the unclaimed limit.

Checking Your Own Token

GET /api/public/v1/auth/me reports the authenticated account, its granted scopes, and claim status in one call — the fastest way to debug a 403:
curl -s https://app.opentrain.ai/api/public/v1/auth/me \
  -H "Authorization: Bearer $OT_API_TOKEN"

Authentication

Where tokens come from, the claim ceremony, and token rotation.

Human Approvals

Why money-moving calls return 202 and how to track the human co-sign.