Skip to main content
The complete opentrain command surface on one page. Every command supports --json (raw API response) and --base-url <url> (override the API origin); those two flags are omitted from the per-command tables below. Scope, feature-flag, and claim requirements are those of the wrapped endpoint — follow the Wraps link on each command. See the CLI overview for installation, credentials, and conventions.

Global

opentrain --version      # print the CLI version (also: opentrain version)
opentrain help           # print usage (also: opentrain --help, or no arguments)

Auth and Identity

whoami

opentrain whoami [--json]
Alias of auth status — prints the authenticated user, token label, scopes, and account type. Wraps GET /api/public/v1/auth/me.

auth register

opentrain auth register [--agent-name <name>] [--org-name <name>] [--force] [--json]
Creates an anonymous agent account and saves the pre-claim ot_pat_ token plus the claim token to the credentials file. Fails if credentials already exist unless --force is passed.
FlagDescription
--agent-name <name>Display name for the agent identity (alias --name)
--org-name <name>Name for the employer organization created with the account (alias --organization-name)
--forceOverwrite existing saved credentials
opentrain auth register --agent-name "Hiring agent" --org-name "Acme Research" --json
Wraps POST /api/agent/identity.

auth claim

opentrain auth claim --email <human-email> [--claim-token <token>] [--json]
Emails the human owner an invitation to claim the agent-registered account. The claim token defaults to the one saved by auth register.
FlagDescription
--email <address>Required. The human owner’s email address
--claim-token <token>Claim token to use; defaults to the saved one
Wraps POST /api/agent/identity/claim.

auth claim-status

opentrain auth claim-status [--wait] [--timeout <seconds>] [--claim-token <token>] [--json]
Checks whether the human completed the claim. On success the saved token is upgraded automatically to the claimed account’s scopes. Without --wait it checks once and reports pending; with --wait it polls (respecting slow_down backoff) until claimed or the timeout elapses.
FlagDescription
--waitPoll until the claim completes
--timeout <seconds>Give up waiting after this long (default 1800)
--claim-token <token>Claim token to check; defaults to the saved one
opentrain auth claim-status --wait --timeout 600
Wraps POST /api/agent/oauth/token.

auth login

opentrain auth login --api-key <token> [--base-url <url>]
Verifies an existing ot_pat_ token against the API, then saves it to the credentials file.
FlagDescription
--api-key <token>Required. The personal API token (alias --token)
Wraps GET /api/public/v1/auth/me for verification.

auth status

opentrain auth status [--json]
Same as whoami. Wraps GET /api/public/v1/auth/me.

auth logout

opentrain auth logout
Deletes the saved credentials file. Purely local — it does not revoke the token; use tokens revoke for that.

Jobs

jobs draft create

opentrain jobs draft create --description <text> [--title <title>] [--external-id <id>] [--idempotency-key <key>] [--json]
Creates an unpublished draft from a plain-text job description (the primary workflow) or a structured payload. The response includes the validation state: each missing field carries an ask: question, type, allowed enum values, and the field name to set with jobs draft update. Alias: jobs create.
FlagDescription
--description <text>Plain-text job description or project brief
--description-file <path>Read the description from a file instead
--canonical-file <path>JSON file with an OpenTrain canonical job object
--payload-file <path>JSON file with a supported import payload (e.g. schema.org JSON-LD)
--title <title>Title to prepend to plain-text descriptions
--external-id <id>Source-system id for audit
--idempotency-key <key>Reuse to avoid duplicate drafts on retry
opentrain jobs draft create \
  --description "Label 5,000 street-scene images with bounding boxes. Native Spanish speakers, \$14/hr." \
  --json
Wraps POST /api/public/v1/job-drafts.

jobs draft update

opentrain jobs draft update --job-id <id> --set <field=value> [--set <field=value> ...] [--json]
Fills in or corrects fields on an unpublished draft. --set values are auto-coerced (numbers, booleans, JSON arrays; everything else stays a string); repeat the loop until the draft is publish-ready. Alias: jobs update.
FlagDescription
--job-id <id>Required. The draft job id
--set <field=value>Set one field (repeatable) — use the set: field names from the validation output
--patch-file <path>JSON file with a field patch object (alternative to --set)
--patch-json <json>Inline JSON patch object (alternative to --set)
opentrain jobs draft update --job-id <JOB_ID> \
  --set paymentType=PAY_PER_HOUR --set pricePerHour=14 --json
Wraps PATCH /api/public/v1/job-drafts/{jobId}.

jobs list

opentrain jobs list [--status <status>] [--limit <n>] [--cursor <cursor>] [--json]
Lists your own jobs with publish state, proposal counts, and live URLs.
FlagDescription
--status <status>Filter: DRAFT, OPEN, ONGOING, COMPLETED, ARCHIVED, PENDING_APPROVAL
--limit <n>Page size (max 100, default 25)
--cursor <cursor>Pagination cursor from the previous page
Wraps GET /api/public/v1/jobs/mine.
opentrain jobs search [--q <text>] [--category <slug>] [--language <lang>] [--country <iso>] [--pay-type <type>] [--limit <n>] [--cursor <cursor>] [--json]
Searches the public marketplace (all live jobs, not just yours). No token required.
FlagDescription
--q <text>Free-text query
--category <slug>Category filter
--language <lang>Language filter
--country <iso>ISO country code
--pay-type <type>PAY_PER_HOUR, FIXED_PRICE, or PAY_PER_LABEL
--limit <n> / --cursor <c>Pagination
Wraps GET /api/public/v1/jobs.

jobs publish

opentrain jobs publish --job-id <id> [--json]
Publishes a publish-ready draft live on the marketplace, running the same validation and moderation pipeline as the in-app flow. Subject to daily publish limits. Wraps POST /api/public/v1/jobs/{id}/publish.

jobs invite

opentrain jobs invite --job-id <id> --freelancer-id <id> [--json]
Invites an AI trainer to a published job, creating a proposal. Idempotent — re-inviting returns the existing proposal. Wraps POST /api/public/v1/jobs/{id}/invites.

jobs close

opentrain jobs close --job-id <id> [--json]
Archives a published job: it stops accepting proposals and leaves public listings. Existing contracts are unaffected. Idempotent. Wraps POST /api/public/v1/jobs/{id}/close.

jobs update-published

opentrain jobs update-published --job-id <id> --set <field=value> [--set <field=value> ...] [--json]
Patches a live (OPEN) job using the same field names and --set/--patch-file/--patch-json flags as jobs draft update. The revised listing is re-moderated; a blocked result unpublishes the job back to draft. Wraps PATCH /api/public/v1/jobs/{id}.

Proposals and Candidates

proposals list

opentrain proposals list --job-id <id> [--status <status>] [--limit <n>] [--cursor <cursor>] [--json]
Lists proposals for a job with statuses, bids, and AI-interview scores — the ranking view for deciding who to interview or hire.
FlagDescription
--job-id <id>Required. The job to list proposals for
--status <status>Filter, e.g. UNREVIEWED, SHORTLISTED, HIRED, DECLINED
--limit <n> / --cursor <c>Pagination (max 100, default 25)
Wraps GET /api/public/v1/jobs/{id}/proposals.

proposals get

opentrain proposals get --proposal-id <id> [--interview] [--json]
Reads the full candidate evaluation: bid, AI-interview score and summary, verification, assessment results, and contract state. --interview also fetches the sanitized AI-interview transcript. Alias: proposal get. Wraps GET /api/public/v1/proposals/{proposalId} and, with --interview, GET /proposals/{proposalId}/interview.

proposals hire

opentrain proposals hire --proposal-id <id> --amount <usd> [--milestone-name <name>] [--milestone-description <text>] [--due-date <iso>] [--confirm-not-fit-override] [--json]
Requests a hire — never hires anyone or moves money. Records a pending approval and returns 202 with an approvalUrl a signed-in human must confirm in the OpenTrain app; on confirm, the contract is created and the first escrow milestone funded. A 409 payment_method_required includes a billingUrl a human must visit. Alias: proposal hire.
FlagDescription
--proposal-id <id>Required. The proposal to hire from
--amount <usd>Required. First milestone amount in USD
--milestone-name <name>Short milestone name
--milestone-description <text>What the first milestone delivers
--due-date <iso>Milestone due date (ISO 8601)
--confirm-not-fit-overrideConfirm hiring a candidate previously marked “Not a fit” after a 409 not_fit_confirmation_required
opentrain proposals hire --proposal-id <PROPOSAL_ID> --amount 500 \
  --milestone-name "First batch" --milestone-description "First 1,000 labeled images" --json
Wraps POST /api/public/v1/proposals/{proposalId}/hire.

freelancers get

opentrain freelancers get --id <user-id-or-slug> [--json]
Reads a masked AI trainer profile (skills, stats, work and labeling experience, education, reviews) by user id or public profile slug. Names stay masked to first name + last initial; personal contact details are never returned. Alias: freelancer get. Wraps GET /api/public/v1/freelancers/{idOrSlug}.

Messages

messages list

opentrain messages list [--filter all|job|proposal] [--unread-only] [--limit <n>] [--cursor <cursor>] [--json]
Lists conversation summaries you participate in, with unread counts and the latest message. Wraps GET /api/public/v1/messages.

messages unread

opentrain messages unread [--filter all|job|proposal] [--limit <n>] [--cursor <cursor>] [--json]
Shorthand for messages list --unread-only — only conversations with unread messages.

messages read

opentrain messages read --conversation-id <id> [--limit <n>] [--direction older|newer] [--cursor <cursor>] [--json]
Reads messages inside one conversation, paginated in either direction. Wraps GET /api/public/v1/messages?conversationId=....

messages send

opentrain messages send --conversation-id <id> --content <text> [--json]
Sends a plain-text message (max 10,000 chars) into an existing conversation. Use --content-file <path> to read the message body from a file. Wraps POST /api/public/v1/messages.

messages start-proposal-thread

opentrain messages start-proposal-thread --proposal-id <id> [--json]
Opens (or fetches) the pre-hire direct-message thread for a proposal — idempotent get-or-create, employer side only. Returns the conversationId to use with messages send. Wraps POST /api/public/v1/proposals/{proposalId}/conversation.

Contracts and Milestones

contracts list

opentrain contracts list [--job-id <id>] [--status active|ended] [--json]
Lists contracts (hired AI trainers) with milestones, the masked freelancer identity (first name + last initial), and the post-hire job DM conversationId. Wraps GET /api/public/v1/contracts.

contracts get

opentrain contracts get --contract-id <id> [--json]
Reads one contract in detail. Alias: contract get. Wraps GET /api/public/v1/contracts/{contractId}.

contracts end

opentrain contracts end --contract-id <id> [--json]
Ends a contract. With no funded milestones it ends immediately; with funded escrow at stake it returns a pending approval with an approvalUrl a human must confirm. Wraps POST /api/public/v1/contracts/{contractId}/end.

milestones create

opentrain milestones create --contract-id <id> --description <text> [--name <name>] [--amount <usd>] [--volume <n>] [--due-date <iso>] [--json]
Adds an unfunded milestone to an active contract — no money moves at creation.
FlagDescription
--contract-id <id>Required. The contract to add to
--description <text>Required. What the milestone delivers
--name <name>Short milestone name
--amount <usd>Milestone amount in USD
--volume <n>Unit volume for per-unit milestones (e.g. label count)
--due-date <iso>Due date (ISO 8601)
Wraps POST /api/public/v1/contracts/{contractId}/milestones.

milestones fund

opentrain milestones fund --milestone-id <id> [--json]
Requests escrow funding. No money moves: the response is a pending approval with an approvalUrl a signed-in human must open and confirm (expires in ~72 h). Track with approvals get or updates poll. Wraps POST /api/public/v1/milestones/{milestoneId}/fund.

milestones approve

opentrain milestones approve --milestone-id <id> [--json]
Requests release of a funded milestone’s escrow. Same human co-sign pattern as milestones fund. Wraps POST /api/public/v1/milestones/{milestoneId}/approve.

approvals get

opentrain approvals get --approval-id <id> [--json]
Checks a pending human co-sign approval: pending, confirmed, declined, or expired, plus the execution result once confirmed. Alias: approval get. Wraps GET /api/public/v1/approvals/{approvalId}.

Credits

credits show

opentrain credits show [--json]
Reads the prepaid credit balance — available vs reserved (escrow-held) — plus recent ledger activity. Alias: credits balance. Wraps GET /api/public/v1/credits.

credits ledger

opentrain credits ledger [--cursor <cursor>] [--limit <n>] [--json]
Pages the full ledger of top-ups, holds, releases, captures, refunds, and adjustments, newest first. Wraps GET /api/public/v1/credits/ledger.

credits top-up

opentrain credits top-up --amount <usd> [--json]
Starts a Stripe Checkout top-up (1010–10,000) and returns a checkoutUrl a human must open and pay — no money moves from the command itself. Wraps POST /api/public/v1/credits/top-ups.

credits top-up-status

opentrain credits top-up-status --top-up-id <id> [--json]
Checks a top-up: pending (awaiting payment), completed, canceled, or expired. Wraps GET /api/public/v1/credits/top-ups/{topUpId}.

Updates

updates poll

opentrain updates poll [--cursor <cursor>] [--limit <n>] [--json]
Polls the account delta feed — small ID-only events for new proposals, messages, contracts, milestone changes, and pending payments. Persist the returned nextCursor and pass it as --cursor on the next poll. Wraps GET /api/public/v1/updates.

Webhooks

webhooks create

opentrain webhooks create --url <url> --events <type,...> [--json]
Subscribes an HTTPS URL to platform events. The response includes the signing secret once — store it and use it to verify the X-OpenTrain-Signature header.
FlagDescription
--url <url>Required. HTTPS delivery endpoint
--events <type,...>Required. Comma-separated event types, e.g. proposal.received,message.received
Wraps POST /api/public/v1/webhooks.

webhooks list

opentrain webhooks list [--json]
Lists subscriptions with status (ACTIVE or DISABLED). Secrets are never included. Wraps GET /api/public/v1/webhooks.

webhooks get

opentrain webhooks get <webhook-id> [--json]
Reads one subscription, including disable details after sustained delivery failure. The id can also be passed as --webhook-id. Alias: webhook get. Wraps GET /api/public/v1/webhooks/{webhookId}.

webhooks delete

opentrain webhooks delete <webhook-id> [--json]
Deletes a subscription and stops deliveries. Deleting and re-creating is also how a DISABLED subscription is resumed (the new one gets a new secret). Wraps DELETE /api/public/v1/webhooks/{webhookId}.

Tokens

tokens list

opentrain tokens list [--json]
Lists every API token on the account — names, scopes, status, expiry. Secrets are never returned. Wraps GET /api/public/v1/tokens.

tokens revoke

opentrain tokens revoke --token-id <id> [--json]
Revokes a token by id — immediate and irreversible. Revoking the token the CLI is currently using breaks subsequent commands. Wraps DELETE /api/public/v1/tokens/{tokenId}.
There is no tokens create command — mint new tokens over HTTP with POST /tokens or in the OpenTrain app’s settings.

Team

team show

opentrain team show [--json]
Reads the employer team: organization, members with roles, and pending invites. Wraps GET /api/public/v1/team.

team invite

opentrain team invite --email <address> [--json]
Emails a human an invitation to join the team with shared job and inbox access. Wraps POST /api/public/v1/team/invites.

Payments

payments pending

opentrain payments pending [--json]
Reads pending payment and milestone state visible to the token owner. Read-only — it never releases funds or changes payment settings. Wraps GET /api/public/v1/payments/pending.