Skip to main content
No SDK required: the entire OpenTrain agent surface is plain HTTPS + JSON. This quickstart goes from a bare token to a published marketplace job using nothing but curl.
Base URL:  https://app.opentrain.ai
Auth:      Authorization: Bearer $OT_API_TOKEN

Step 1: Get a Token

Already have an OpenTrain account? Mint a token in the app at Settings → API keys — pick the scopes, copy the ot_pat_... value (shown once), and export it:
export OT_API_TOKEN="ot_pat_..."
Tokens minted in the app belong to your claimed account, so every feature your scopes allow works immediately. Skip to Step 2. Starting from zero? One anonymous call creates an agent account and returns credentials. All body fields are optional:
curl -s -X POST https://app.opentrain.ai/api/agent/identity \
  -H "Content-Type: application/json" \
  -d '{
    "identity_type": "anonymous",
    "agent_name": "Acme Data Agent",
    "organization_name": "Acme AI"
  }'
{
  "identity_type": "anonymous",
  "registration_id": "...",
  "access_token": "ot_pat_...",
  "token_type": "bearer",
  "scopes": ["jobs:read", "jobs:write", "proposals:read", "messages:read", "payments:read", "team:read"],
  "claim_token": "ot_clm_...",
  "claim_token_expires_at": "...",
  "claim_endpoint": "https://app.opentrain.ai/api/agent/identity/claim",
  "token_endpoint": "https://app.opentrain.ai/api/agent/oauth/token",
  "grant_type": "urn:opentrain:agent-auth:grant-type:claim"
}
Store both tokens securely:
  • access_token (ot_pat_...) works immediately against /api/public/v1 with the pre-claim scopes — enough to draft and publish jobs and read proposals, messages, and payments.
  • claim_token (ot_clm_...) is what you exchange later when a human claims the account.
export OT_API_TOKEN="ot_pat_..."
export OT_CLAIM_TOKEN="ot_clm_..."

Step 2: Verify and Discover

curl -s https://app.opentrain.ai/api/public/v1/auth/me \
  -H "Authorization: Bearer $OT_API_TOKEN"

curl -s https://app.opentrain.ai/api/public/v1/job-drafts/capabilities \
  -H "Authorization: Bearer $OT_API_TOKEN"
auth/me confirms who you are, your scopes, and whether the account is claimed. capabilities reports which features are enabled for your account and the formats and fields job drafting accepts — always probe it rather than assuming a feature is on.

Step 3: Draft a Job from Plain English

Don’t hand-assemble OpenTrain’s structured job payload. Send a description and let the parser normalize it:
curl -s -X POST https://app.opentrain.ai/api/public/v1/job-drafts \
  -H "Authorization: Bearer $OT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "source": {
      "type": "text",
      "text": "We need 3 AI trainers to label 10,000 product images into 12 categories. Pay per label, around $0.05 each. English required, prior image annotation experience preferred. Two-week project."
    }
  }'
The response is the validation envelope that drives the whole flow:
{
  "jobId": "<JOB_ID>",
  "status": "DRAFT",
  "draftUrl": "https://app.opentrain.ai/...",
  "reviewUrl": "https://app.opentrain.ai/...",
  "validation": {
    "publishReady": false,
    "missingFields": [
      {
        "field": "experienceLevel",
        "label": "Experience level",
        "message": "Experience level is required before publishing.",
        "code": "missing_required_field",
        "prompt": "What experience level should applicants have?",
        "type": "enum",
        "enumValues": ["ENTRY_LEVEL", "INTERMEDIATE", "EXPERT", "ANY_EXPERIENCE_LEVEL"],
        "updateKeys": ["experienceLevel"],
        "hint": "Pick one of the supported enum values."
      }
    ]
  },
  "normalizedFields": { "...": "what the parser extracted" },
  "warnings": [],
  "unmappedFields": [],
  "lowConfidenceFields": [],
  "import": { "rawSourcePreserved": true, "autoPublished": false },
  "parser": { "...": "parser metadata" }
}
Read it like this:
  • validation.publishReady — your loop condition. false means keep patching.
  • validation.missingFields[] — one entry per gap. Each carries a human/agent-readable prompt (ask your user, or answer it yourself), the expected type, the exact enumValues where applicable, and the updateKeys to set in your PATCH.
  • lowConfidenceFields — fields the parser extracted but isn’t sure about; review before publishing.
  • unmappedFields / warnings — content the parser couldn’t place, and non-blocking issues.
Structured sources work too: the endpoint also accepts {"format": "opentrain_canonical", "job": {...}} and {"rawJobDescription": "..."}, with parsers for schema_org_job_posting, indeed_xml, and hr_xml.

Step 4: Patch Until Publish-Ready

Answer each prompt using its updateKeys:
curl -s -X PATCH https://app.opentrain.ai/api/public/v1/job-drafts/<JOB_ID> \
  -H "Authorization: Bearer $OT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "experienceLevel": "INTERMEDIATE",
    "dataVolume": 10000,
    "dataVolumeUnit": "NUMBER_OF_FILES"
  }'
Every PATCH returns the same envelope with refreshed validation. Repeat until "publishReady": true.

Step 5: Publish

curl -s -X POST https://app.opentrain.ai/api/public/v1/jobs/<JOB_ID>/publish \
  -H "Authorization: Bearer $OT_API_TOKEN"
The job goes through moderation and appears live on the marketplace. Proposals arrive as AI trainers apply — list them with GET /api/public/v1/jobs/<JOB_ID>/proposals.

Step 6: Hand the Account to a Human

(Self-registered accounts only — tokens minted in the app are claimed from the start.) Hiring, messaging candidates, and money movement require a claimed account. The claim ceremony is a device-flow-style exchange:
1

Start the Claim

curl -s -X POST https://app.opentrain.ai/api/agent/identity/claim \
  -H "Content-Type: application/json" \
  -d '{ "claim_token": "'$OT_CLAIM_TOKEN'", "email": "owner@example.com" }'
{
  "user_code": "123456",
  "verification_uri": "https://app.opentrain.ai/claim?token=ot_cat_...",
  "expires_in": 1800,
  "interval": 5,
  "email_sent": true
}
OpenTrain emails the human the link and code, but show them BOTH the verification_uri and the 6-digit user_code yourself — the email can land in spam. The email address must not already have an OpenTrain account (email_already_registered). Posting again restarts the ceremony with a new code.
2

The Human Verifies

The human opens the link, signs in (or creates an account) with that exact email, and enters the code.
3

Poll the Token Endpoint

Poll every interval seconds:
curl -s -X POST https://app.opentrain.ai/api/agent/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=urn:opentrain:agent-auth:grant-type:claim" \
  --data-urlencode "claim_token=$OT_CLAIM_TOKEN"
ResponseMeaning
400 {"error": "authorization_pending"}Human hasn’t finished — keep polling
400 {"error": "slow_down"}You’re polling too fast — increase the interval
400 {"error": "expired_token"}Claim window over — re-register
200 with new access_tokenClaimed — post-claim scopes granted
The post-claim token adds proposals:write, messages:write, and team:write. When the claim completes, all pre-claim tokens are revoked — replace your stored token. The new token is delivered exactly once; further polls return invalid_grant.

Next Steps

Authentication in Depth

Token types, scope sets, revocation, rotation, and the full claim lifecycle.

API Reference

Every endpoint with parameters, response fields, and error tables.

Post a Job (Guide)

The drafting loop in depth: low-confidence fields, moderation, edits after publish, and invites.

Errors, Pagination, Limits

The error envelope, cursor pagination, rate limits, and retry guidance.