Skip to main content
POST
/
api
/
public
/
v1
/
contracts
/
{contractId}
/
end
curl -sS -X POST https://app.opentrain.ai/api/public/v1/contracts/<CONTRACT_ID>/end \
  -H "Authorization: Bearer $OT_API_TOKEN"
{
  "ok": true,
  "contractId": "<CONTRACT_ID>",
  "status": "ended"
}
Ends a contract. The outcome depends on whether funded escrow is at stake:
  • No funded milestones — the end executes immediately and returns 200 with status: "ended".
  • Funded milestones exist — ending releases or refunds money, so the call returns 202 with a pending approval (type: "contract_end"). A signed-in human must open approval.approvalUrl and confirm before the contract ends. Approvals expire after ~72 hours. Track the outcome via GET /approvals/{id} or the approval.confirmed event on GET /updates.
The request has no body. Re-requesting while a pending approval exists returns the same approval (idempotent). Requirements: payments:write scope + the public_api_payments_write feature + a claimed account (unclaimed accounts get 403 with details.reason: "account_claim_required" and a claimUrl). The contract must be yours and still active.

Request

contractId
string
required
The contract to end.

Response — 200 (ended directly)

ok
boolean
true.
contractId
string
The ended contract’s ID.
status
string
ended.

Response — 202 (human co-sign required)

approval
object
The pending approval, in the same shape as GET /approvals/{id}: {id, type: "contract_end", status: "pending", contractId, milestoneId: null, jobId, proposalId: null, approvalUrl, expiresAt, resolvedAt, result, createdAt}.
message
string
Explains that a signed-in human must confirm ending the contract in the OpenTrain app.

Errors

StatuscodeMeaning
400BAD_REQUESTMissing contractId
401UNAUTHORIZEDMissing or invalid token
403FORBIDDENMissing payments:write scope, public_api_payments_write disabled, or account not claimed (details.reason: "account_claim_required", details.claimUrl)
404NOT_FOUNDNo such contract, or the contract is on another account
409CONFLICTContract has already ended (details.reason: "contract_ended")
curl -sS -X POST https://app.opentrain.ai/api/public/v1/contracts/<CONTRACT_ID>/end \
  -H "Authorization: Bearer $OT_API_TOKEN"
{
  "ok": true,
  "contractId": "<CONTRACT_ID>",
  "status": "ended"
}