Skip to content
OpenTrain AIOpenTrain AIOpenTrain AIDocs

Ask OpenTrain

Answers from the documentation, with sources.

What would you like to do with OpenTrain?

AI answers can be mistaken. Check the linked sources. Don’t include private account information.

Open app

DEVELOPER DOCUMENTATION

Poll Updates

The authenticated delta feed: fetch every account event since a cursor in one call — proposals, messages, contracts, milestones, payments, approvals.

GET/api/public/v1/updates

Fetches everything that happened on your account since your last poll in one cheap call, instead of re-reading every resource. Events are returned oldest first; pass the returned nextCursor on the next poll to receive only newer events. Payloads carry IDs only — fetch details with the matching read endpoint.

Webhooks push these same events; the recommended architecture uses the webhook as the trigger and this feed as the source of truth — see stay in sync. For tokenless public marketplace changes, use GET /jobs/changes instead.

Requirements: at least one of proposals:read, messages:read, or payments:read — each event type is visible only with its scope (below). Works pre-claim.

typeRequired scopeKey data fields
proposal.receivedproposals:readproposalId, jobId
proposal.status_changedproposals:readproposalId, status
message.receivedmessages:readconversationId, messageId
contract.createdpayments:readcontractId, jobId, proposalId
milestone.status_changedpayments:readmilestoneId, contractId, status
payment.pendingpayments:readinvoiceId, milestoneId, contractId
approval.confirmedpayments:readapprovalId, status
contract.budget_state_changedpayments:readcontractId, previousState, state, paymentType, fundedVolume, consumedVolume, remainingVolume, consumedFraction

Event types your token cannot read are silently filtered out of the feed; they are not an error. A token with none of the three scopes gets 403.

cursorstringquery

The nextCursor from your previous poll (a numeric event ID). Omit on the first poll to start from the beginning of your account’s event history.

limitintegerquery
Default: 50

Max events to return, 1–200.

eventsobject[]
event
idstring

Monotonically increasing numeric event ID — this is what cursors point at.

typestring

One of the eight event types above.

apiVersionstring

v1.

createdAtstring

ISO timestamp of the event.

resourceIdstring

ID of the primary affected resource (e.g. the proposal ID for proposal.received).

jobIdstring | null

The related job, when the event is job-linked.

dataobject

IDs-only payload (see the table above) — never message bodies or other content.

nextCursorstring | null

Persist this and pass it as cursor on the next poll. When the page is empty it echoes your input cursor (null on a first poll with no events).

hasMoreboolean

true when more events already exist beyond this page — poll again immediately with nextCursor.

StatuscodeMeaning
400BAD_REQUESTcursor is not a numeric event ID (details.field: "cursor"), or limit outside 1–200 (details.field: "limit")
401UNAUTHORIZEDMissing or invalid token
403FORBIDDENToken has none of proposals:read, messages:read, payments:read (details.requiredScopes, details.grantedScopes)