Limits
Everything on this page is a limit the server actually enforces. Where a limit you might expect does not exist, this page says so rather than guessing, because a plausible invented number is worse than an honest gap.
Rate limits
Section titled “Rate limits”Requests are metered with a token bucket per durable authority, not per IP, per key, or per connection. Rotating a credential does not get you a fresh bucket.
| Bucket | Partition | Burst | Sustained per minute |
|---|---|---|---|
Authenticated /v1 |
Your installation (or membership) within one organization | 120 | 600 |
| Pre-authentication edge | Client IP | 600 | 3000 |
| Token issuance | Client IP | 60 | 60 |
Those are the shipped defaults, and an operator can tune every one of them. Do not hardcode them. Every authenticated response tells you where you stand:
RateLimit-Policy: "wamp-cloud";q=600;w=60;wamp-burst=120RateLimit: "wamp-cloud";r=117;t=59q is the sustained quota, w the window in seconds, wamp-burst the bucket
capacity, r your remaining tokens, t seconds until refill. One request costs
one token regardless of what it does.
When the bucket is empty you get 429 with Retry-After, Cache-Control: no-store, and retryAfterSeconds in the body. If the limiter itself faults, the
API fails closed with 503 cloud_rate_limit_unavailable and Retry-After: 1
rather than becoming unmetered.
The two discovery endpoints — GET /v1/openapi.json and GET /v1/docs — are
unauthenticated and are not metered by either bucket.
This is operational flow control, not a commercial entitlement or an SLA.
Quotas
Section titled “Quotas”| Quota | Limit | Enforcement |
|---|---|---|
| Active (non-archived) sessions per organization | 100 | Counted at session creation; 429 cloud_session_limit_reached |
| Webhook endpoints per app installation | 5 | Checked at registration; 409 endpoint_limit_reached |
| Event families per webhook endpoint | 1–4 | Request validation; 400 invalid_event_families |
The session quota counts every session that has not been archived, whatever its
state. Archiving is a soft delete (DELETE /v1/sessions/{sessionId} → 204), and
it is how you free quota. Discovery reports the current value as
capabilities.limits.maxActiveSessions, so read it rather than assuming 100.
There is no quota on turns per session, on runs over time, on artifacts per session, or on events per session.
Request payload maximums
Section titled “Request payload maximums”| Input | Limit | On violation |
|---|---|---|
| Any JSON request body | 1 MB | 413 (with a non-standard body shape — see Errors) |
task on session creation |
100 000 characters | 400 invalid_request |
message on a turn |
100 000 characters | 400 invalid_request |
title on a session |
160 characters | 400 invalid_request |
model |
128 characters | 400 invalid_request |
runtime |
64 characters, ^[a-z0-9][a-z0-9-]*$ |
400 invalid_request |
Publication title |
256 characters, no CR or LF | 400 invalid_request |
Publication body |
65 536 characters | 400 invalid_request |
Merge commitTitle |
256 characters, no CR or LF | 400 invalid_request |
Merge commitMessage |
65 536 characters | 400 invalid_request |
| Any HTTPS URL in a request | 2048 characters | 400 invalid_request |
origin.tenantKey |
128 characters | 400 invalid_request |
origin.objectType |
64 characters | 400 invalid_request |
origin.objectId |
256 characters | 400 invalid_request |
origin.endUserId |
128 characters | 400 invalid_request |
origin.label |
256 characters | 400 invalid_request |
replyTo.interactionId |
255 characters | 400 invalid_request |
Opaque paging cursor |
1024 characters | 400 invalid_request |
Two limits interact and both apply: a task of 100 000 characters is legal by
character count, but if it is heavily multibyte or heavily escaped the encoded
JSON can still exceed the 1 MB body cap and fail with 413 before validation
ever runs. capabilities.limits.maxMessageCharacters reports the character cap.
Page sizes
Section titled “Page sizes”| Endpoint | limit range |
Default |
|---|---|---|
GET /v1/sessions |
1–100 | 50 |
GET …/publications |
1–100 | 50 |
GET …/events |
1–100 | 100 |
GET …/turns |
1–100 | 100 |
GET …/artifacts |
1–100 | 100 |
limit=0, limit=101 and a non-numeric limit are each 400 invalid_request,
not clamped. GET …/interactions is not paged and returns the whole open set.
Discovery reports the ceiling as capabilities.limits.maxPageSize.
Artifact sizes
Section titled “Artifact sizes”These are enforced when the server retains an artifact, so exceeding them does
not fail your API call — it means the artifact is not kept, and you receive a
wamp.artifact.omitted event instead.
| Artifact | Limit |
|---|---|
| Presented file, each | 32 MiB (33 554 432 bytes) |
| Presented files, total per run | 64 MiB (67 108 864 bytes) |
| Presented files, count per run | 24 |
Run summary (result.summary) |
100 000 bytes |
| Conversation checkpoint | 16 MiB, with a 16 KiB manifest |
| Workspace checkpoint | 256 MiB (268 435 456 bytes), with a 64 KiB manifest |
The two checkpoint figures are reported by discovery as
capabilities.artifacts.conversationCheckpointMaxBytes and
capabilities.artifacts.workspaceCheckpointMaxBytes; the workspace figure also
appears as capabilities.environments[0].workspace.maxCheckpointBytes. The
presented-file caps are not advertised anywhere in the API, which is why they
are worth reading here: a run that writes a 40 MiB build output produces an
omission event rather than an artifact, and nothing about the API call tells you
that in advance.
A workspace checkpoint over its cap is the difference between a session that can
continue after its sandbox ends and one that cannot — see continuation on the
session resource.
Sandbox timeouts
Section titled “Sandbox timeouts”| Limit | Value |
|---|---|
| Maximum lifetime of one sandbox lease | 3600 seconds (1 hour) |
| Extension beyond that ceiling | Not possible |
One hour is a hard constant, not a configurable policy and not something a caller can raise — a keepalive can push a lease’s deadline forward only up to the ceiling measured from when the lease started. A session that outlives it rolls onto a new lease rather than extending the old one.
You observe this through the session’s workspace object, which has exactly
three states:
workspace.state |
Meaning |
|---|---|
unavailable |
No sandbox is attached |
attached |
A sandbox is attached; expiresAt is its deadline |
expired |
The lease deadline has passed |
An expired workspace does not destroy the session. Read continuation to
find out whether another turn can resume the work, and at what fidelity.
There is no configurable per-run wall-clock limit, no per-turn timeout, and no
idle timeout you can set. An agent that stops on a budget reports it as
stopReason on the terminal run event (time_budget, cost_budget,
turn_budget, max_turns).
Concurrency ceilings
Section titled “Concurrency ceilings”These are enforced by database constraints, which means they hold under concurrent requests from any number of your processes — not just per process.
| Ceiling | Value | Result of exceeding |
|---|---|---|
| Non-terminal runs per session | 1 | 409 cloud_run_in_progress |
| Non-terminal publications per session | 1 | 409 cloud_publication_in_progress |
| Active merges per publication | 1 | 409 |
| Successful merges per publication, ever | 1 | 409 |
| Turns resolving one interaction | 1 | 409 cloud_interaction_conflict |
The first is the one that shapes your architecture: one run at a time per session means one session per concurrent task. Do not multiplex unrelated work into a single session to save quota — with a 100-session cap per organization, that cap is also your concurrency ceiling, and the honest way to raise throughput is to finish and archive sessions.
There is no documented cap on concurrent sessions executing at once beyond the 100 active-session quota, and no per-organization sandbox count you can query.
Retry behavior the server applies for you
Section titled “Retry behavior the server applies for you”| Command | Attempts | Backoff |
|---|---|---|
| Run dispatch to a sandbox | 10, then the run fails with dispatch_attempts_exhausted |
2 s doubling, capped at 5 minutes |
| Webhook delivery | 6, then permanently failed | 1, 2, 4, 8, 16, 32 minutes (capped at 6 hours) |
| Publication | Not capped | Server-managed |
Merge (mode: when_ready) |
Not capped | Re-attempted about every 30 seconds while the pull request stays open and unchanged |
A run exposes attempts so you can see retries happening without doing
anything. Publications and merges expose attempts too, and because neither is
capped, your side owns the timeout for a when_ready merge. Decide how long
you are willing to wait and stop polling when it elapses; the server will keep
waiting indefinitely.
Webhook deliveries time out after 10 seconds measured on your response, which is why a receiver should acknowledge before doing work.
Credential lifetimes
Section titled “Credential lifetimes”| Credential | Lifetime |
|---|---|
| App assertion (the JWT you sign) | Up to 600 seconds. A longer one is rejected |
| Installation access token | 600 seconds |
| Clock skew tolerated on an assertion | 30 seconds |
| Webhook signature replay window | 300 seconds |
Cache an installation token for its lifetime minus a margin — a minute is
comfortable — and re-mint on 401. Do not mint one per request.
Repository review budgets
Section titled “Repository review budgets”GET /v1/sessions/{sessionId}/repository/review is bounded, and it tells you
when it hit a bound rather than silently shortening the diff.
| Budget | Value |
|---|---|
| Files in one review | 500 |
| Total path bytes | 128 KiB |
| Total patch bytes | 1 MiB |
When the patch budget is exhausted, patchTruncated is true and individual
files lose their patch field; the file list stays complete. Exceeding the file
budget is 413 review_too_large.
Transcript budgets
Section titled “Transcript budgets”| Budget | Value |
|---|---|
| Transcript facts captured per run | 10 000 |
wamp.message.created text |
16 384 characters |
| Activity or runtime name | 128 characters |
Beyond the fact budget you receive one wamp.timeline.truncated event carrying
the count that was dropped. Text over its cap arrives with truncated: true.
Retention
Section titled “Retention”This is where the honest answer is “not documented”:
| Data | What is verifiable |
|---|---|
| Sessions | Archiving is a soft delete. Nothing in the API purges an archived session, and no retention window is published |
| Events | There is no retention field on an event and no documented expiry. A cursor therefore stays valid across long outages, but no guaranteed window is published |
| Artifact bytes | Bytes can be reclaimed. The manifest survives, state becomes pruned, and the content endpoint answers 410 cloud_artifact_pruned. The window before that happens is not documented |
| Conversation and workspace checkpoints | Can be reclaimed, which is reflected in the session’s continuation |
| Webhook deliveries | Permanently failed after 6 attempts |
Do not design around any particular retention duration, in either direction. The two robust patterns:
- If you need artifact bytes durably, copy them. Fetch the content while
stateisavailableand store it yourself; verify with the manifest’ssha256. - If you need transcripts durably, page events into your own store. Treat the API’s log as a repair channel for recent state, not as your archive.
Conversely, do not assume anything is deleted on a schedule either. If you have a data-retention obligation, archive the session and confirm the deployment’s actual policy with your operator — it is an operational property, not an API guarantee.
Related
Section titled “Related”- Errors — the codes each of these limits produces.
- API conventions — paging parameters and the rate-limit headers.
- Capabilities operations — reading the advertised limits at runtime instead of hardcoding them.
- Receive webhooks — delivery limits from the receiver’s side.