Skip to content
OpenTrain AIOpenTrain AIOpenTrain AIDocs

Ask OpenTrain

Answers from the documentation, with sources.

What would you like to do with OpenTrain?

AI answers can be mistaken. Check the linked sources. Don’t include private account information.

Open app

DEVELOPER DOCUMENTATION

Quickstart: HTTP API

Mint or register a token, draft a job, and publish it with plain HTTP requests.

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

Already have an OpenTrain account? Mint a token in the app at Settings → API keys — choose Full access for a trusted ongoing agent or Fine-grained for a narrow integration, copy the ot_pat_... value (shown once), and export it:

Terminal window
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:

Terminal window
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.
Terminal window
export OT_API_TOKEN="ot_pat_..."
export OT_CLAIM_TOKEN="ot_clm_..."
Terminal window
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.

Don’t hand-assemble OpenTrain’s structured job payload. Send a description and let the parser normalize it:

Terminal window
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.

Answer each prompt using its updateKeys:

Terminal window
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.

Terminal window
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.

(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:

Start the Claim

Terminal window
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.

The Human Verifies

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

Poll the Token Endpoint

Poll every interval seconds:

Terminal window
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.