Polling and idempotency
Polling is the source of truth
Section titled “Polling is the source of truth”GET /serving-tasks/?status=offered is how you discover work. Webhooks, if
registered, are a latency optimization — never truth.
Poll continuously while your fleet is on duty.
Every list response carries an ETag. Send it back as If-None-Match; an
unchanged list returns 304 Not Modified with no body. This makes tight
polling loops nearly free for both sides — always implement it.
Rate limits
Section titled “Rate limits”Rates are strict and keyed per credential:
| Surface | Limit |
|---|---|
| Task polling | 60/minute (~1/s) |
| Task claim/report | 120/minute |
| Heartbeat | 30/minute |
A controller that needs more is misbehaving, not busy. 429 responses carry
Retry-After — honor it.
Idempotency
Section titled “Idempotency”Every mutation accepts an Idempotency-Key header (or a
client_idempotency_key body field). A duplicate request with the same key
returns the original outcome and records nothing new.
Always send one. Networks fail mid-request; with a key you can retry freely and never double-apply a claim or a report.
Use one key per action, not one per task: reuse the same key for retries
of the same logical action, and a fresh key for each new one. A key already
spent on a different transition of the same task is refused with 409
(idempotency_key_reused). Keys are capped at 128 characters.
Request bodies are bounded too — the optional telemetry and heartbeat
summary objects must be JSON objects of at most 4 KB, nested no more than
5 levels, with no more than 50 entries in any one object or array.
The error envelope
Section titled “The error envelope”Every refused request returns one shape:
{ "detail": "Human-readable explanation.", "code": "machine_readable_code" }| HTTP status | Codes |
|---|---|
400 |
unknown_status, status_not_reportable, unknown_failure_code |
404 |
task_not_found |
403 |
fleet standing lost — certification, activation, or entitlement |
409 |
already_claimed, robotics_paused, platform_frozen, arrival_dwell_pending, idempotency_key_reused, task_<current_status> ordering conflicts |
410 |
offer_expired |
Branch on code, never on the detail text — detail wording may change;
codes are contractual.