Skip to main content

API overview

This page covers the ground rules that apply to every Crusoe Agent Platform API call: where to send requests, how to authenticate, and how errors, paging, rate limits, and request IDs work. Every other API reference page assumes you have read this one.

Base URL

The platform is in alpha, and there is no public API hostname yet. The web console at https://console.codyhill.dev is the public front door; automation reaches the REST APIs through an endpoint your administrator gives you, or through a kubectl port-forward tunnel. All examples in these docs use the $CAI_API environment variable so you can paste them unchanged.

There are two API services:

ServiceWhat it servesEnv var convention
agent-engine-apiAgents, functions, sessions, projects, organizations, users, API keys, service accounts, quotas, audit, invitations, search, MCP servers$CAI_API
serverless-apiScale-to-zero container services and their triggers$CAI_SERVERLESS_API

Set them up like this (an admin-provided URL works the same way — just export it):

# agent-engine-api (the core API)
kubectl -n cai-system port-forward svc/agent-engine-api 8080:8080 &
export CAI_API=http://localhost:8080

# serverless-api (scale-to-zero container services)
kubectl -n cai-system port-forward svc/serverless-api 9000:8080 &
export CAI_SERVERLESS_API=http://localhost:9000

The platformctl CLI does this port-forwarding for you automatically when neither variable is set. See the CLI overview.

Alpha honesty

Big clouds give you a stable public API hostname; we don't yet. When a public API endpoint ships, $CAI_API in every example will simply point at it — nothing else changes.

Authentication

Every authenticated request carries one header:

Authorization: Bearer <token-or-api-key>

Four credential shapes are accepted, dispatched by prefix:

CredentialShapeLifetimeWhere it comes from
API keycai_<keyid>_<secret>Until revoked (optional expiry, 1–3650 days)Personal keys or service accounts
Session tokenOpaque signed token12 hours, not revocablePOST /v1/auth/login
Automation tokenCluster machine credentialLong-livedKubernetes Secret cai-automation-token in namespace cai-agents
NothingReaches only /healthz, agent invoke (the data plane, open by default), invitation preview/accept, and the public embed routes

Authorization is live: your roles and grants are re-read from the platform database on every request, so a demotion or key revocation takes effect on your very next call. The token's role claim is only a hint.

An unauthenticated call to a management endpoint returns 401 with this exact message:

this is a management endpoint and requires authentication. Sign in (POST /v1/auth/login) and send 'Authorization: Bearer <token>', or use the automation token: kubectl -n cai-agents get secret cai-automation-token -o jsonpath='{.data.token}' | base64 -d

An invalid or expired credential is treated as no credential: identity-optional routes still work, and management routes return that same 401.

The platform splits its surface into two planes:

  • Management plane — anything that creates, changes, or inspects state (deploy, logs, secrets, members, keys). Always requires a credential.
  • Data plane — talking to an agent (POST /v1/agents/{name}/invoke). Open to anonymous callers by default; the platform operator can close it by setting INVOKE_AUTH_REQUIRED=true. Writing to an agent's memory bank (memorize) always requires a credential.

For the full authentication model, including service-account fences and the console's cookie flow, see API authentication.

Error envelope

Every non-2xx response from either API uses one JSON envelope:

{"error": "<message>", "request_id": "<id>"}
  • error is a plain-English message meant to be shown to a human. These docs quote them verbatim so you can search for them.
  • request_id matches the X-Request-Id response header (it is omitted only if the request-ID middleware is absent).
  • Unrouted paths (404) and wrong methods (405) render this same envelope — you never get bare HTML from the API.

Request IDs

Every response carries an X-Request-Id header. Error bodies echo it as request_id. Include it when you report a problem — it lets an operator find your exact request in the logs.

Pagination

Every list endpoint pages the same way:

ParameterTypeDefaultNotes
page_sizeinteger query param50Maximum 200. limit is accepted as an alias. A value over 200 is rejected, not clamped.
page_tokenstring query paramOpaque cursor from the previous page. Tamper-evident and bound to the list that issued it.

Responses include next_page_token; it is empty (or absent) on the last page. Keep requesting with the returned token until it comes back empty.

Errors:

  • 400 — page_size must be between 1 and 200
  • 400 — page_token is invalid or was issued for a different list; start from the first page

Rate limits

LimitValueWhat you see
General API rate limit (agent-engine-api)Per-principal token bucket (operator-tuned via RATELIMIT_RPS / RATELIMIT_BURST); anonymous callers are limited per IP429
Sign-in throttle10 attempts per IP and 50 per account, per 15 minutes429 — too many sign-in attempts; try again in <duration> with a Retry-After header
serverless-api rate limit100 requests/sec, burst 200, per principal per API replica; /healthz exempt429

The 404-vs-403 rule

This rule surprises people coming from other clouds, so learn it once:

  • Any request against a project you hold no grant on returns 404 not found — never 403. Project existence is deliberately not discoverable, and "someone else's agent" looks identical to "no such agent".
  • 403 appears only when you already have some access but lack the role for that specific action. Examples: a project member calling the admin-only serverless DELETE; a user who still owes a first-login password change (403 — password change required before using this API on every management route); a service-account key trying to mint credentials.

Do not write retry logic that treats 403 as "the resource exists but I can't see it" — on project paths you will get 404 for that case.

Where to go next