Skip to main content
POST
/
api
/
partner
/
v1
/
contracts
/
{contractId}
/
usage
curl -sS -X POST "https://app.opentrain.ai/api/partner/v1/contracts/<CONTRACT_ID>/usage" \
  -H "Authorization: Bearer $OT_PARTNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entries": [
      {
        "workDate": "2026-06-12",
        "totalSeconds": 14400,
        "tasksCompleted": 52,
        "labelsCompleted": 410,
        "externalReportId": "daily-report-8841"
      }
    ]
  }'
{
  "contractId": "<CONTRACT_ID>",
  "accepted": 1,
  "budget": {
    "contractId": "<CONTRACT_ID>",
    "paymentType": "PAY_PER_HOUR",
    "state": "OK",
    "fundedVolume": 40,
    "fundedAmountUsd": 560,
    "consumed": { "seconds": 100800, "hours": 28, "labels": 410, "tasks": 52 },
    "consumedVolume": 28,
    "remainingVolume": 12,
    "consumedFraction": 0.7,
    "activeMilestone": {
      "id": "<MILESTONE_ID>",
      "name": "Week 2",
      "amountUsd": 280,
      "volume": 20,
      "status": "ACTIVE_FUNDED"
    },
    "lastUsageAt": "2026-06-12T18:00:00.000Z"
  }
}
Reports work done on your platform against an OpenTrain contract. Entries are cumulative per-worker, per-day totals: each entry replaces the stored totals for its (worker, workDate), so re-POSTing the same report — or a corrected one — is idempotent and never double counts. Fields omitted from an entry keep their previously stored values. The response includes the recomputed budget. Crossing the 80% / 100% consumption thresholds emits milestone.budget_low / milestone.budget_depleted webhooks. Requirements: usage:write scope, and the contract’s job must be referenced by a project link on the calling token’s install.

Request

contractId
string
required
The OpenTrain contract to attribute usage to.
entries
object[]
required
1–100 usage entries. One entry per (worker, day).

Response

contractId
string
The contract the usage was recorded against.
accepted
integer
Number of entries upserted.
budget
object
The recomputed budget — same shape as GET /contracts/{contractId}/budget.

Errors

StatuscodeMeaning
400BAD_REQUESTInvalid entries: missing/malformed workDate, future date, totalSeconds out of range, negative counts, more than 100 entries, or workerOpentrainUserId not a participant on this contract
401UNAUTHORIZEDMissing, invalid, or revoked token
403FORBIDDENToken lacks usage:write
404NOT_FOUNDContract not found, or its job is not linked by this install
409CONFLICTContract has no hired AI trainer to attribute usage to
curl -sS -X POST "https://app.opentrain.ai/api/partner/v1/contracts/<CONTRACT_ID>/usage" \
  -H "Authorization: Bearer $OT_PARTNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entries": [
      {
        "workDate": "2026-06-12",
        "totalSeconds": 14400,
        "tasksCompleted": 52,
        "labelsCompleted": 410,
        "externalReportId": "daily-report-8841"
      }
    ]
  }'
{
  "contractId": "<CONTRACT_ID>",
  "accepted": 1,
  "budget": {
    "contractId": "<CONTRACT_ID>",
    "paymentType": "PAY_PER_HOUR",
    "state": "OK",
    "fundedVolume": 40,
    "fundedAmountUsd": 560,
    "consumed": { "seconds": 100800, "hours": 28, "labels": 410, "tasks": 52 },
    "consumedVolume": 28,
    "remainingVolume": 12,
    "consumedFraction": 0.7,
    "activeMilestone": {
      "id": "<MILESTONE_ID>",
      "name": "Week 2",
      "amountUsd": 280,
      "volume": 20,
      "status": "ACTIVE_FUNDED"
    },
    "lastUsageAt": "2026-06-12T18:00:00.000Z"
  }
}