curl -sS https://app.opentrain.ai/api/public/v1/contracts/<CONTRACT_ID> \
-H "Authorization: Bearer $OT_API_TOKEN"
{
"contract": {
"id": "<CONTRACT_ID>",
"status": "active",
"title": "Spanish Sentiment Labeling Contract",
"jobId": "<JOB_ID>",
"proposalId": "<PROPOSAL_ID>",
"paymentType": "FIXED_PRICE",
"rateUsd": 300,
"estimatedTotalUsd": 300,
"estimatedVolume": null,
"hasActiveMilestone": true,
"startDate": "2026-06-12T09:30:00.000Z",
"endDate": null,
"createdAt": "2026-06-12T09:30:00.000Z",
"updatedAt": "2026-06-12T09:30:00.000Z",
"freelancer": {
"userId": "<FREELANCER_ID>",
"displayName": "Maria G.",
"country": "Spain",
"profilePath": "/profile/maria-g"
},
"milestones": [
{
"id": "<MILESTONE_ID>",
"name": "First labeling batch",
"description": "Label the first 5,000 posts per the guidelines",
"status": "ACTIVE_FUNDED",
"amountUsd": 300,
"volume": null,
"milestoneNumber": 1,
"dueDate": "2026-07-01T00:00:00.000Z",
"pendingApproval": false,
"needsReview": false,
"invoiceId": "<INVOICE_ID>",
"createdAt": "2026-06-12T09:30:00.000Z"
}
],
"jobDmConversationId": "<CONVERSATION_ID>",
"budget": {
"contractId": "<CONTRACT_ID>",
"paymentType": "FIXED_PRICE",
"state": "OK",
"fundedVolume": 0,
"fundedAmountUsd": 300,
"consumed": { "seconds": 0, "hours": 0, "labels": 0, "tasks": 0 },
"consumedVolume": 0,
"remainingVolume": 0,
"consumedFraction": 0,
"activeMilestone": {
"id": "<MILESTONE_ID>",
"name": "First labeling batch",
"amountUsd": 300,
"volume": null,
"status": "ACTIVE_FUNDED"
},
"lastUsageAt": null
}
}
}
Contracts
Get Contract
Read one contract in detail: status, milestone timeline, the hired AI trainer, budget state, and the post-hire conversation ID.
GET
/
api
/
public
/
v1
/
contracts
/
{contractId}
curl -sS https://app.opentrain.ai/api/public/v1/contracts/<CONTRACT_ID> \
-H "Authorization: Bearer $OT_API_TOKEN"
{
"contract": {
"id": "<CONTRACT_ID>",
"status": "active",
"title": "Spanish Sentiment Labeling Contract",
"jobId": "<JOB_ID>",
"proposalId": "<PROPOSAL_ID>",
"paymentType": "FIXED_PRICE",
"rateUsd": 300,
"estimatedTotalUsd": 300,
"estimatedVolume": null,
"hasActiveMilestone": true,
"startDate": "2026-06-12T09:30:00.000Z",
"endDate": null,
"createdAt": "2026-06-12T09:30:00.000Z",
"updatedAt": "2026-06-12T09:30:00.000Z",
"freelancer": {
"userId": "<FREELANCER_ID>",
"displayName": "Maria G.",
"country": "Spain",
"profilePath": "/profile/maria-g"
},
"milestones": [
{
"id": "<MILESTONE_ID>",
"name": "First labeling batch",
"description": "Label the first 5,000 posts per the guidelines",
"status": "ACTIVE_FUNDED",
"amountUsd": 300,
"volume": null,
"milestoneNumber": 1,
"dueDate": "2026-07-01T00:00:00.000Z",
"pendingApproval": false,
"needsReview": false,
"invoiceId": "<INVOICE_ID>",
"createdAt": "2026-06-12T09:30:00.000Z"
}
],
"jobDmConversationId": "<CONVERSATION_ID>",
"budget": {
"contractId": "<CONTRACT_ID>",
"paymentType": "FIXED_PRICE",
"state": "OK",
"fundedVolume": 0,
"fundedAmountUsd": 300,
"consumed": { "seconds": 0, "hours": 0, "labels": 0, "tasks": 0 },
"consumedVolume": 0,
"remainingVolume": 0,
"consumedFraction": 0,
"activeMilestone": {
"id": "<MILESTONE_ID>",
"name": "First labeling batch",
"amountUsd": 300,
"volume": null,
"status": "ACTIVE_FUNDED"
},
"lastUsageAt": null
}
}
}
Reads one contract in full, in the same shape as the entries on
GET /contracts plus two detail-only fields: jobDmConversationId — the post-hire 1:1 thread with the hired AI trainer, usable with GET /messages and POST /messages — and budget, the funded-vs-consumed budget snapshot.
Unknown contract IDs and contracts on other accounts both return 404, so contract IDs cannot be probed.
Requirements: payments:read scope. Works pre-claim.
Request
The contract to read.
Response
Hide contract
Hide contract
Contract ID.
active or ended.Contract display title.
The job this contract belongs to.
The proposal the hire came from.
FIXED_PRICE, PAY_PER_HOUR, or PAY_PER_LABEL.Contract rate in USD.
Estimated total contract value in USD.
Estimated unit volume for per-unit contracts.
true when a funded milestone is in progress.ISO contract start timestamp.
ISO contract end timestamp,
null while active.ISO creation timestamp.
ISO last-change timestamp.
The hired AI trainer:
{userId, displayName, country, profilePath}. displayName is masked to first name + last initial ("Maria G.") — full last names and emails never appear (see privacy).Milestone timeline — same entry shape as on
GET /contracts: {id, name, description, status, amountUsd, volume, milestoneNumber, dueDate, pendingApproval, needsReview, invoiceId, createdAt}.The post-hire 1:1 conversation with the hired AI trainer. Pass to
GET /messages to read it or POST /messages to message them.Funded milestones vs work consumed:
{contractId, paymentType, state, fundedVolume, fundedAmountUsd, consumed: {seconds, hours, labels, tasks}, consumedVolume, remainingVolume, consumedFraction, activeMilestone, lastUsageAt}. state is OK, LOW (≥ 80% of funded volume consumed), or DEPLETED (≥ 100%); volume is hours for PAY_PER_HOUR, labels for PAY_PER_LABEL; FIXED_PRICE contracts track progress only and always report OK. State changes emit the contract.budget_state_changed event — when it lands on LOW or DEPLETED, the usual move is to propose funding the next milestone. null when the budget cannot be computed. Only this detail endpoint returns it, not the list.Errors
| Status | code | Meaning |
|---|---|---|
401 | UNAUTHORIZED | Missing or invalid token |
403 | FORBIDDEN | Missing payments:read scope |
404 | NOT_FOUND | No such contract, or the contract is on another account (details: {resource: "contracts", contractId}) |
curl -sS https://app.opentrain.ai/api/public/v1/contracts/<CONTRACT_ID> \
-H "Authorization: Bearer $OT_API_TOKEN"
{
"contract": {
"id": "<CONTRACT_ID>",
"status": "active",
"title": "Spanish Sentiment Labeling Contract",
"jobId": "<JOB_ID>",
"proposalId": "<PROPOSAL_ID>",
"paymentType": "FIXED_PRICE",
"rateUsd": 300,
"estimatedTotalUsd": 300,
"estimatedVolume": null,
"hasActiveMilestone": true,
"startDate": "2026-06-12T09:30:00.000Z",
"endDate": null,
"createdAt": "2026-06-12T09:30:00.000Z",
"updatedAt": "2026-06-12T09:30:00.000Z",
"freelancer": {
"userId": "<FREELANCER_ID>",
"displayName": "Maria G.",
"country": "Spain",
"profilePath": "/profile/maria-g"
},
"milestones": [
{
"id": "<MILESTONE_ID>",
"name": "First labeling batch",
"description": "Label the first 5,000 posts per the guidelines",
"status": "ACTIVE_FUNDED",
"amountUsd": 300,
"volume": null,
"milestoneNumber": 1,
"dueDate": "2026-07-01T00:00:00.000Z",
"pendingApproval": false,
"needsReview": false,
"invoiceId": "<INVOICE_ID>",
"createdAt": "2026-06-12T09:30:00.000Z"
}
],
"jobDmConversationId": "<CONVERSATION_ID>",
"budget": {
"contractId": "<CONTRACT_ID>",
"paymentType": "FIXED_PRICE",
"state": "OK",
"fundedVolume": 0,
"fundedAmountUsd": 300,
"consumed": { "seconds": 0, "hours": 0, "labels": 0, "tasks": 0 },
"consumedVolume": 0,
"remainingVolume": 0,
"consumedFraction": 0,
"activeMilestone": {
"id": "<MILESTONE_ID>",
"name": "First labeling batch",
"amountUsd": 300,
"volume": null,
"status": "ACTIVE_FUNDED"
},
"lastUsageAt": null
}
}
}
⌘I