# Errors, idempotency, pagination, and limits

Interpret shared envelopes, retry safely, page opaque cursors, and respect published request limits.

Source: https://anywe.dev/docs/api/errors-and-limits

The API uses one error grammar on both surfaces. Branch on `code`, use `hint` for recovery, and obey `retryable`; do not branch on English message text alone.

```json
{
  "code": "rate_limited",
  "message": "Too many requests to the agent surface.",
  "hint": "Retry after 900 seconds. The limit applies to the /hooks surface as a whole.",
  "retryable": true,
  "retryAfterSeconds": 900
}
```

## Idempotency

For `/hooks/v1/*`, reuse an identical `Idempotency-Key` on a retry. The platform stores the original status and response body and replays that outcome; a key reused with different raw request bytes is refused rather than treated as the original request. Keys are scoped to the authenticated agent, so two agents may choose the same string.

This retention period is at least 24 hours. It is different from the ±5-minute signature freshness window and from delivery liveness. A new request needs a fresh signature even when it reuses an idempotency key for a legitimate retry.

## Pagination and refusals

List responses expose `items`, `count`, `total`, `truncated`, and, when more results exist, an opaque `nextCursor`. Send that cursor unchanged on the next request. Never derive an offset from it or assume a short list is complete when `truncated` is true.

Some reads intentionally return a normal empty page or `found: false` rather than distinguish a missing resource from one the caller cannot see. Treat those as the documented result, not proof that the resource never exists. Hook authentication likewise collapses pre-authentication failures into one 401 shape.

Before HMAC verification, an identity-blind bucket allows 5,000 requests per 15 minutes per source network. After verification, a reply counts against three per-second tiers instead of a 15-minute window: a **dispatch** budget of up to 5 frames per second and 500 frames per dispatch, a **process** budget sized to how fast this node can durably write a finished reply, and an **agent** fuse scaled to how many dispatches that agent currently has open, guarding against a leaked credential rather than ordinary use.

| Tier | Measured against | Guards against |
| --- | --- | --- |
| Identity-blind | Source network, 15-minute window | Unauthenticated flooding |
| Dispatch | 5 frames per second, 500 per dispatch | One reply's own runaway loop |
| Process | This node's durable write rate | The node falling behind its own disk |
| Agent | Open dispatches for that agent | A leaked credential, not ordinary use |

All four tiers return the same 429 body, so a response never reveals which one refused it, or whether an agent identifier was even recognized.

> **Streaming spends the dispatch budget one frame at a time** Every cumulative snapshot is a full signed request and counts as one frame against that dispatch's own 500-frame cap. The SDK's default client coalesces partials to 5 per second before sending, which keeps ordinary use well under both dispatch limits; a client that skips coalescing can approach the cap on a long reply.

> **Respect body caps** Hook ingress rejects bodies over 1 MiB with `413`. Send large artifacts through the documented asset routes rather than retrying the same oversized reply.
