Every number the platform enforces, in one place. See Errors for what each status code means.
Rate limits
Counting is per fixed one-minute bucket, not a sliding window: a bucket resets on the minute boundary, so a caller can spend its whole budget at 12:00:59 and the same budget again at 12:01:00. A 429 carries Retry-After in seconds until the next boundary.
The limiter fails open. If its own storage errors, the request is allowed. Do not treat these numbers as a security control — they bound accidental load, not a determined caller.
| Endpoint | Limit | Counted per |
|---|---|---|
POST /api/agents/{id}/runs/trigger | 20/min | agent |
POST /api/agents/{id}/runs/trigger-and-wait | 5/min | agent |
POST /api/agents/{id}/runs/trigger-and-download | 5/min | agent |
POST /api/agents/{id}/runs/register | 20/min | agent |
POST /api/agents/{id}/runs/ingest | 20/min | agent |
POST /api/agents/{id}/runs/webhook/{token} | 60/min | agent |
POST /api/agents/{id}/runs/webhook-and-wait/{token} | 10/min | agent |
POST /api/runs/{id}/callback | 20/min | agent |
POST /api/runs/{id}/waitpoint | 20/min | agent |
GET /api/credentials/{id}/resolve | none | — |
POST /api/zones/agents/{id}/runs (and the sync variants) | 30/min | user |
POST /api/zones/agents/{id}/runs/{runId}/waitpoint | 20/min | user + agent |
GET /api/zones/agents/{id}/credentials/{key} | 60/min | user + agent |
The synchronous variants are deliberately stricter than their async counterparts: each call holds a connection open for the duration of the wait.
The callback limit is the one that bites. 20/min per agent is shared between progress callbacks and waitpoints, so a task emitting one output() per processed item will exceed it on any batch larger than about twenty per minute. Batch progress — report every N items or every few seconds, not every item. Runtime metadata is not rate-limited and is the better tool for per-item progress; see Run lifecycle and progress.
Timeouts
| Value | Configurable | |
|---|---|---|
| Queued → failed | 10 minutes from run creation | Deployment-wide only; no per-agent override |
| Running → failed | 2 hours from start | Per agent, via Max run duration |
| Max run duration ceiling | 24 hours | — |
| Status poll interval | 60 seconds | Per agent, 10–3600 seconds |
Queued and running are separate clocks. Time spent queued does not count toward the running timeout, and the per-agent Max run duration does not extend the queued window.
The queued timeout is a hard 10 minutes and it constrains your task's queue settings. A run that waits longer than that in the runtime's queue — the normal effect of a runtime-side concurrency limit — is marked
failedwith "Run never started (queued timeout exceeded)", and the runtime may then execute it anyway, leaving the platform's record disagreeing with what happened. Do not rely on runtime queueing to serialise runs; serialise inside the task instead.
Synchronous wait windows
trigger-and-wait, trigger-and-download, webhook-and-wait and the zone equivalents all resolve their wait as the smallest of:
- the caller's
timeoutMs, defaulting to 60 seconds when omitted; - the agent's Max run duration;
- a platform ceiling of 5 minutes.
So a caller asking for 10 minutes gets 5. Exceeding the window is not an error — the response is 202 with timedOut: true, the run continues, and the caller falls back to polling. Anything that routinely runs longer than five minutes should use the asynchronous endpoint.
Token lifetimes
| Token | Lifetime | Refresh |
|---|---|---|
credentialsToken | 60 minutes | None — minted once, at trigger or register |
| Zone JWT | 5 minutes | Minted fresh on every proxied request |
agent_api_key, ingest_api_key, webhook_token, zone_jwt_secret | Until rotated | Rotate in the platform UI |
The credentials token bounds when /resolve may be called, not how long a run may live. Resolve everything you need at the start of the run and keep the values in memory; a task may then run for hours. A task that first resolves a credential at minute 90 gets a 401.
The zone JWT's five minutes are not a session length. The platform mints a new one on every request it proxies to the zone, and the token never reaches the browser, so a tab left open for hours does not expire — each navigation and server action arrives with a fresh token. Two consequences: a zone must use the token from the current request, and stashing one to reuse later will start failing at the five-minute mark. If a long-idle tab does break, it is the visitor's platform session that ended, not the zone token; the symptom is a platform 404 rather than a 401, because the platform stops proxying and the path no longer resolves.
Waitpoints have no ceiling while paused — but the run's clock never stopped. The timeout is whatever the task sets when it creates the token, the platform stores no deadline, and a run may sit in waiting indefinitely without the poller touching it.
What is not suspended is the running timeout. started_at is stamped once, when the run first starts, and is never reset — so on resume the elapsed time is measured from the original start, pause included. A run that waited three days resumes already three days over a two-hour Max run duration, and is failed on the next poll tick with "Run exceeded running timeout". Since Max run duration is capped at 24 hours, no configuration makes a pause longer than a day survivable.
The practical rule: pause for minutes or hours, never for days. A long approval should not be modelled as a paused run at all — see Run lifecycle and progress.
One further exception: the pause is only free when the runtime reports the run as waiting, which is what a native waitpoint does. A task that reports waiting to the platform while continuing to execute is still running upstream, so the running timeout applies throughout the pause as well.
Payload caps
| Limit | |
|---|---|
| Webhook request body | 1 MiB → 413 |
Ingest error string | 4096 characters |
Input and output JSONB have no hard cap, which is not an invitation. Large output_data slows every read of that run and has to render on the run detail page — keep bulk data in your own storage and reference it.
Retention
Runs and their input/output snapshots are retained indefinitely.
Execution logs are not stored by the platform — they are fetched from the runtime on demand, so log availability is bounded by your runtime's own retention, not by ours.