Docs

Pagination and Errors

Every list endpoint on the Amdahl Platform API follows the same shape, and every error response follows the same envelope. Code once against the conventions on this page and you will not have to special-case individual tools.

List endpoint conventions

Every list tool (artifacts.list, agents.list, sessions, webhooks.list, audit_log.list, knowledge_base.list, context_entries.list, and so on) accepts the same base query parameters:

ParameterTypeDefaultMaxPurpose
limitinteger50200Page size
offsetinteger0n/aZero-indexed offset

And returns the same envelope:

json
{
  "items": [ ... ],
  "limit": 50,
  "offset": 0,
  "total": 147,
  "has_more": true
}
  • items: array of rows for this page.
  • limit / offset: the values actually applied (clamped if you overshot).
  • total: total matching rows across all pages. Authoritative; safe for UI pagers.
  • has_more: true if offset + items.length < total.

Paging example

bash
# First page
curl -s "$AMDAHL_BASE/artifacts?limit=50&offset=0" \
  -H "Authorization: Bearer $AMDAHL_KEY"

# Next page
curl -s "$AMDAHL_BASE/artifacts?limit=50&offset=50" \
  -H "Authorization: Bearer $AMDAHL_KEY"

A client-side pager can simply walk offset by limit while has_more is true.

Filtering conventions

Most list tools expose a common set of filters:

ParameterApplies toExample
statusArtifacts, agents, content sessions, webhooks?status=draft
created_at_gte / created_at_lteEverything with a created_at?created_at_gte=2026-01-01T00:00:00Z
updated_at_gte / updated_at_lteEverything with an updated_at?updated_at_gte=2026-04-01T00:00:00Z
qSearch-capable lists (knowledge base, audit log)?q=churn

Tool-specific filters (for example artifact_type on artifacts, profile on agents, event on webhook deliveries) are documented on each tool's reference page under api-reference/tools/.

Sorting

Every list endpoint sorts by created_at DESC by default: newest first. Tools that need a different default override this in their reference documentation. To request an explicit sort, pass order_by and order_dir:

code
?order_by=updated_at&order_dir=asc

Fields you can sort on are listed per tool. Passing an unsupported field returns invalid_input.

Error envelope

Every non-2xx response uses the same JSON envelope:

json
{
  "error": {
    "code": "invalid_input",
    "message": "Human readable description of what went wrong.",
    "details": {
      "field": "scopes",
      "issue": "must be a non-empty array"
    }
  }
}
  • code: a machine-readable string from the catalogue below.
  • message: a short human-readable description. Safe to surface in UIs.
  • details: optional object with structured context. Shape depends on code.

Clients should branch on code, never on the exact text of message. HTTP status is included but is a coarser signal than code.

Standard error codes

CodeHTTPWhenExpected client reaction
unauthenticated401Missing or invalid credentialReauthenticate, do not retry the exact call
forbidden403Credential valid but lacks required scope or access to the resourceCheck details.missing_scope, rotate with higher scopes or stop
not_found404Target resource does not exist or is outside the caller's businessTreat as terminal; do not retry
invalid_input400Request body failed schema validationFix the payload; do not retry the same body
invalid_argument400Syntactically valid request whose semantics are wrong (bad sort field, out-of-range values)Fix arguments; do not retry the same call
conflict409State conflict (for example revoking a key already revoked, double-approving an outline)Refetch current state; reconcile
rate_limited429Rate limit exceededBack off; see rate-limits.md
internal500Server errorRetry with backoff for 5xx only

The full, continuously-generated catalogue of codes plus details keys lives at api-reference/errors.md.

Retry strategy

  • Retry: internal (5xx) and rate_limited (429). Use exponential backoff with jitter. Start at 500ms, double each attempt, cap at 30 seconds, give up after 5 attempts unless your workload can tolerate more.
  • Do not retry: every other 4xx. invalid_input, forbidden, not_found, and conflict all mean "this exact call will not succeed". Retrying wastes quota and can trigger rate_limited on top of the original failure.
  • Honor Retry-After. When present on a 429 or 503 response (in seconds), wait that long before the next attempt.

Example rate_limited body:

json
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded for tool data.query.",
    "details": {
      "limit": 60,
      "window_seconds": 60,
      "retry_after_seconds": 12
    }
  }
}

Idempotency

POST endpoints that create resources (artifacts.create, context_entries.create, webhooks.create, agents.start, pages.create) are not yet idempotent at the server level. A successful retry will create a duplicate row.

Until a server-side Idempotency-Key header lands (tracked in changelog.md as a planned feature), deduplicate on the client:

  • For artifacts and context entries: include a caller-generated key in metadata.client_request_id and, before each retry, list with a filter on that key.
  • For agents: poll agent_run.list with a stable tag before starting; if you find an active run, do not start a new one.

Tools that patch (artifacts.update), delete (artifacts.archive), or mark-complete are naturally idempotent and safe to retry.

Reference