# Pagination and idempotency (/docs/api/pagination-and-idempotency)



List endpoints return a `{ data, hasMore }` envelope, and create-style
POST endpoints accept an `Idempotency-Key` header so retries don't
duplicate work.

## Pagination [#pagination]

Resource list endpoints (sandboxes, code contexts, artifacts, previews,
projects, API keys, Workspace Runs) return the full visible collection in a
`{ data, hasMore }` envelope. `hasMore` is currently always `false` and is
reserved for future paging.

```json
{
  "data": [],
  "hasMore": false
}
```

The log and event streams page by a `nextSeq` sequence number rather than a
cursor. Command logs and workspace-run events take `limit` and `afterSeq`
query parameters and return a `nextSeq` field to fetch the next page. See
[Commands](/docs/api/commands) and
[Workspace Runs](/docs/api/workspace-runs) for details.

## Idempotency [#idempotency]

When a POST might have landed but the connection dropped, the
`Idempotency-Key` header makes retries safe: send the same key with the
same request, and the operation runs at most once.

```bash title="Terminal"
curl -X POST https://api.crownest.dev/v1/sandboxes \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0f6b2c1e-create-sandbox-01" \
  -d '{"template": "python-node"}'
```

Choose any unique string per logical operation — a UUID works well. The
CrowNest SDKs generate keys automatically.

### Scope and retention [#scope-and-retention]

A key is scoped to the combination of organization, credential, HTTP
method, route, and key value. The same key value on a different endpoint
or from a different API key is a different idempotency record. Records are
retained for 24 hours; after that, the same key behaves like a new
request.

### Replay and conflict behavior [#replay-and-conflict-behavior]

What happens on a repeated key depends on the state of the original
request:

* **Same key, same request, original completed** — the API replays the
  original response without re-running the operation.
* **Same key, original still in progress** — 409 with code
  `idempotency_request_in_progress`. Wait and retry.
* **Same key, different request body** — 409 with code
  `idempotency_key_reused`. Pick a new key for the new operation.

### Endpoints that accept Idempotency-Key [#endpoints-that-accept-idempotency-key]

The header is honored on POST endpoints that create resources or start
work:

* `POST /v1/sandboxes` — [create a sandbox](/docs/api/sandboxes)
* `POST /v1/sandboxes/{sandboxId}/ttl` —
  [set a sandbox TTL](/docs/api/sandboxes)
* `POST /v1/sandboxes/{sandboxId}/commands` —
  [run a command](/docs/api/commands)
* `POST /v1/sandboxes/{sandboxId}/code/contexts` —
  [create a code context](/docs/api/code)
* `POST /v1/sandboxes/{sandboxId}/code/runs` —
  [run code](/docs/api/code)
* `POST /v1/sandboxes/{sandboxId}/code/runs/stream` —
  [stream code run events](/docs/api/code)
* `POST /v1/sandboxes/{sandboxId}/artifacts` —
  [create an artifact](/docs/api/artifacts)
* `POST /v1/workspace-runs` —
  [create a Workspace Run](/docs/api/workspace-runs)
* `POST /v1/workspace-runs/{workspaceRunId}/start` —
  start a Workspace Run
* `PUT /v1/workspace-runs/{workspaceRunId}/archive` — upload an archive
* `POST /v1/workspace-runs/{workspaceRunId}/archive-transfer` —
  begin a staged upload
* `POST /v1/workspace-runs/{workspaceRunId}/archive/finalize` —
  finalize a staged upload

The deprecated `/extend`, `/commands/run`, and `/commands/start` aliases also
honor `Idempotency-Key` while clients migrate to the canonical endpoints.

Delete and cancel endpoints are idempotent by state, so repeating them
returns the already-deleted or already-canceled resource even without the
header.

## Next steps [#next-steps]

* Apply these patterns in [Sandboxes](/docs/api/sandboxes),
  [Commands](/docs/api/commands), and [Code](/docs/api/code).
* Review retry guidance in [Errors](/docs/api/errors).
