Reporting Usage
POST /contracts/{contractId}/usage (scope usage:write) takes cumulative per-worker, per-day totals — not deltas:
workerOpentrainUserId is optional and defaults to the contract’s hired AI trainer.
The response returns the recomputed budget, so a usage POST doubles as a budget check.
How Budgets Are Computed
A contract’s budget is the sum of its funded milestones’ volume measured against consumed work. What counts as volume depends on the contract’s payment type:paymentType | Funded volume | Consumed by | Depletes? |
|---|---|---|---|
PAY_PER_HOUR | Milestone hours | totalSeconds / 3600 | Yes |
PAY_PER_LABEL | Milestone labels | labelsCompleted | Yes |
FIXED_PRICE | — | Usage is progress reporting only | No — state is always OK |
GET /contracts/{contractId}/budget, and carried on budget webhooks) reports state as one of:
state | Meaning |
|---|---|
OK | Below 80% of funded volume consumed |
LOW | consumedFraction ≥ 0.8 — time to fund the next milestone |
DEPLETED | consumedFraction ≥ 1.0 — funded work is exhausted |
The Depletion Events
Crossing a threshold upward emits a webhook to your endpoints (subscribe to the event types like any other):milestone.budget_low— consumption crossed 80%milestone.budget_depleted— consumption crossed 100%
LOW or DEPLETED do not re-fire it. Funding a new milestone lowers consumedFraction; if usage later crosses a threshold again, the event fires again.
All three budget events share one payload shape (milestone is the active funded milestone, or null if none):
- On
budget_low— surface a warning on the project (“~80% of funded hours used”). The employer is simultaneously notified in OpenTrain to fund the next milestone. - On
budget_depleted— tell the AI trainer in your UI that funded work is exhausted and the next milestone needs funding on OpenTrain before continuing. Both the employer and the AI trainer get OpenTrain notifications saying the same thing. Whether to hard-stop task assignment is your call — OpenTrain doesn’t block your platform.
The Re-Funding Loop
Funding happens on OpenTrain, never through your platform: the employer (or their agent, co-signed by a human) funds the next milestone. The moment that happens you receive:milestone.funded— same payload shape as above, with the fresh budget.statetypically returns toOKandremainingVolumegrows.
milestone.funded in → repeat.
Reconciliation
Like all platform webhooks, budget events are triggers, not the source of truth — there is no replay. PollGET /contracts/{contractId}/budget (scope contracts:read) on a schedule, or rely on the budget object returned by each usage POST, to recover state after downtime.
POST /contracts/{id}/usage
Entry validation, idempotency, and the full response shape.
GET /contracts/{id}/budget
The read-only budget view, field by field.