> ## Documentation Index
> Fetch the complete documentation index at: https://opentrain.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI Command Reference

> Every OpenTrain CLI command with flags, an example, and the API endpoint it wraps — the complete surface on one page.

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](/docs/developers/cli/overview) for installation, credentials, and conventions.

## Global

```bash theme={null}
opentrain --version      # print the CLI version (also: opentrain version)
opentrain help           # print usage (also: opentrain --help, or no arguments)
```

## Auth and Identity

### whoami

```bash theme={null}
opentrain whoami [--json]
```

Alias of `auth status` — prints the authenticated user, token label, scopes, and account type.

Wraps [`GET /api/public/v1/auth/me`](/docs/developers/api-reference/auth/me).

### auth register

```bash theme={null}
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.

| Flag                  | Description                                                                               |
| --------------------- | ----------------------------------------------------------------------------------------- |
| `--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`) |
| `--force`             | Overwrite existing saved credentials                                                      |

```bash theme={null}
opentrain auth register --agent-name "Hiring agent" --org-name "Acme Research" --json
```

Wraps [`POST /api/agent/identity`](/docs/developers/api-reference/agent-auth/register).

### auth claim

```bash theme={null}
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`.

| Flag                    | Description                                   |
| ----------------------- | --------------------------------------------- |
| `--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`](/docs/developers/api-reference/agent-auth/claim).

### auth claim-status

```bash theme={null}
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.

| Flag                    | Description                                      |
| ----------------------- | ------------------------------------------------ |
| `--wait`                | Poll 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  |

```bash theme={null}
opentrain auth claim-status --wait --timeout 600
```

Wraps [`POST /api/agent/oauth/token`](/docs/developers/api-reference/agent-auth/token).

### auth login

```bash theme={null}
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.

| Flag                | Description                                            |
| ------------------- | ------------------------------------------------------ |
| `--api-key <token>` | **Required.** The personal API token (alias `--token`) |

Wraps [`GET /api/public/v1/auth/me`](/docs/developers/api-reference/auth/me) for verification.

### auth status

```bash theme={null}
opentrain auth status [--json]
```

Same as `whoami`. Wraps [`GET /api/public/v1/auth/me`](/docs/developers/api-reference/auth/me).

### auth logout

```bash theme={null}
opentrain auth logout
```

Deletes the saved credentials file. Purely local — it does not revoke the token; use [`tokens revoke`](#tokens-revoke) for that.

## Jobs

### jobs draft create

```bash theme={null}
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`.

| Flag                        | Description                                                         |
| --------------------------- | ------------------------------------------------------------------- |
| `--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                            |

```bash theme={null}
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`](/docs/developers/api-reference/job-drafts/create).

### jobs draft update

```bash theme={null}
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`.

| Flag                  | Description                                                                        |
| --------------------- | ---------------------------------------------------------------------------------- |
| `--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`)                                  |

```bash theme={null}
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}`](/docs/developers/api-reference/job-drafts/update).

### jobs list

```bash theme={null}
opentrain jobs list [--status <status>] [--limit <n>] [--cursor <cursor>] [--json]
```

Lists your own jobs with publish state, proposal counts, and live URLs.

| Flag                | Description                                                                     |
| ------------------- | ------------------------------------------------------------------------------- |
| `--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`](/docs/developers/api-reference/jobs/mine).

### jobs search

```bash theme={null}
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.**

| Flag                           | Description                                       |
| ------------------------------ | ------------------------------------------------- |
| `--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`](/docs/developers/api-reference/jobs/search).

### jobs publish

```bash theme={null}
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`](/docs/developers/api-reference/jobs/publish).

### jobs invite

```bash theme={null}
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`](/docs/developers/api-reference/jobs/invite).

### jobs close

```bash theme={null}
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`](/docs/developers/api-reference/jobs/close).

### jobs update-published

```bash theme={null}
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}`](/docs/developers/api-reference/jobs/update).

## Proposals and Candidates

### proposals list

```bash theme={null}
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.

| Flag                           | Description                                                   |
| ------------------------------ | ------------------------------------------------------------- |
| `--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`](/docs/developers/api-reference/jobs/list-proposals).

### proposals get

```bash theme={null}
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}`](/docs/developers/api-reference/proposals/get) and, with `--interview`, [`GET /proposals/{proposalId}/interview`](/docs/developers/api-reference/proposals/interview).

### proposals hire

```bash theme={null}
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`.

| Flag                             | Description                                                                                          |
| -------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `--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-override`     | Confirm hiring a candidate previously marked "Not a fit" after a `409 not_fit_confirmation_required` |

```bash theme={null}
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`](/docs/developers/api-reference/proposals/hire).

### freelancers get

```bash theme={null}
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}`](/docs/developers/api-reference/freelancers/get).

## Messages

### messages list

```bash theme={null}
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`](/docs/developers/api-reference/messages/list).

### messages unread

```bash theme={null}
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

```bash theme={null}
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=...`](/docs/developers/api-reference/messages/list).

### messages send

```bash theme={null}
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`](/docs/developers/api-reference/messages/send).

### messages start-proposal-thread

```bash theme={null}
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`](/docs/developers/api-reference/proposals/conversation).

## Contracts and Milestones

### contracts list

```bash theme={null}
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`](/docs/developers/api-reference/contracts/list).

### contracts get

```bash theme={null}
opentrain contracts get --contract-id <id> [--json]
```

Reads one contract in detail. Alias: `contract get`.

Wraps [`GET /api/public/v1/contracts/{contractId}`](/docs/developers/api-reference/contracts/get).

### contracts end

```bash theme={null}
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`](/docs/developers/api-reference/contracts/end).

### milestones create

```bash theme={null}
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.

| Flag                   | Description                                            |
| ---------------------- | ------------------------------------------------------ |
| `--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`](/docs/developers/api-reference/contracts/create-milestone).

### milestones fund

```bash theme={null}
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`](/docs/developers/api-reference/milestones/fund).

### milestones approve

```bash theme={null}
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`](/docs/developers/api-reference/milestones/approve).

### approvals get

```bash theme={null}
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}`](/docs/developers/api-reference/approvals/get).

## Credits

### credits show

```bash theme={null}
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`](/docs/developers/api-reference/credits/balance).

### credits ledger

```bash theme={null}
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`](/docs/developers/api-reference/credits/ledger).

### credits top-up

```bash theme={null}
opentrain credits top-up --amount <usd> [--json]
```

Starts a Stripe Checkout top-up ($10–$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`](/docs/developers/api-reference/credits/create-top-up).

### credits top-up-status

```bash theme={null}
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}`](/docs/developers/api-reference/credits/get-top-up).

## Updates

### updates poll

```bash theme={null}
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`](/docs/developers/api-reference/updates/poll).

## Webhooks

### webhooks create

```bash theme={null}
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](/docs/developers/guides/verify-webhook-signatures) the `X-OpenTrain-Signature` header.

| Flag                  | Description                                                                          |
| --------------------- | ------------------------------------------------------------------------------------ |
| `--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`](/docs/developers/api-reference/webhooks/create).

### webhooks list

```bash theme={null}
opentrain webhooks list [--json]
```

Lists subscriptions with status (`ACTIVE` or `DISABLED`). Secrets are never included.

Wraps [`GET /api/public/v1/webhooks`](/docs/developers/api-reference/webhooks/list).

### webhooks get

```bash theme={null}
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}`](/docs/developers/api-reference/webhooks/get).

### webhooks delete

```bash theme={null}
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}`](/docs/developers/api-reference/webhooks/delete).

## Tokens

### tokens list

```bash theme={null}
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`](/docs/developers/api-reference/tokens/list).

### tokens revoke

```bash theme={null}
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}`](/docs/developers/api-reference/tokens/revoke).

<Note>
  There is no `tokens create` command — mint new tokens over HTTP with
  [`POST /tokens`](/docs/developers/api-reference/tokens/create) or in the OpenTrain
  app's settings.
</Note>

## Team

### team show

```bash theme={null}
opentrain team show [--json]
```

Reads the employer team: organization, members with roles, and pending invites.

Wraps [`GET /api/public/v1/team`](/docs/developers/api-reference/team/list).

### team invite

```bash theme={null}
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`](/docs/developers/api-reference/team/invite).

## Payments

### payments pending

```bash theme={null}
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`](/docs/developers/api-reference/payments/pending).
