Docs / Reference

HTTP API

Every /v0 endpoint, its authentication, request fields, response and errors, with curl examples.

The JSON API under /v0 is what the CLI and the MCP server use. Agents without either can call it directly. All examples assume a local server:

export AGENTSERVE=https://agentserve.sh

Authentication#

Every authenticated request sends Authorization: Bearer <token>. Two kinds of token are accepted:

TokenPrefixIssuedGrants
Manage tokenast_anon_Once, in the response that creates a stack.Read, redeploy, extend and delete that one stack, and read its logs and events. Responses report "role": "agent".
API keyas_live_Once, when an account is created, and again each time it is rotated in the dashboard at /account.Create stacks owned by the account, manage every stack the account owns, claim stacks, and GET /v0/me. Responses report "role": "owner".

A stack's manage token keeps working after the stack is claimed, until the owner revokes it from the project's settings page in the dashboard. Creating an anonymous stack needs no token at all. See Security model.

Errors#

Errors are JSON with a single detail string, written so an agent can act on it:

{"detail": "service 'web': static services are served by AgentServe, drop 'start'"}
StatusMeaning
400Missing or invalid parameter.
401Missing Authorization header, or an invalid API key.
403The token doesn't grant access to this stack, or the claim is restricted to another email.
404Stack, service or claim code not found. Deleted stacks are not found.
409The stack's state doesn't allow the operation (expired, claimed, build already running).
413The upload is larger than 50 MB.
422Invalid manifest, archive or TTL. See validation errors.
429Rate limited, or too many unclaimed stacks running from your network. See Limits.

The stack object#

Most endpoints return a stack:

{
  "id": "stk_3f9a1c0b2d4e",
  "name": "todo-app",
  "status": "live",
  "claimed": false,
  "urls": {
    "api": "https://api-todo-app-3f9a.agentserve.sh",
    "web": "https://web-todo-app-3f9a.agentserve.sh"
  },
  "services": [
    {"name": "api", "runtime": "python-3.12", "status": "running", "url": "https://api-todo-app-3f9a.agentserve.sh"},
    {"name": "web", "runtime": "static", "status": "running", "url": "https://web-todo-app-3f9a.agentserve.sh"}
  ],
  "created_at": 1791646331.2,
  "expires_at": 1791649931.2,
  "expired_at": null,
  "retained_until": null,
  "generation": 1,
  "deploying": null,
  "error": null,
  "deploy_error": null
}
FieldTypeMeaning
idstringStack id, stk_ plus 12 hex characters.
namestringStack name.
statusstringbuilding, live, failed or expired. (deleted stacks are not returned.)
claimedbooleanWhether the stack belongs to an account.
urlsobjectService name to public URL.
servicesarrayname, runtime, status (pending, running, restarting or stopped) and url of each service.
created_atnumberUnix time, in seconds.
expires_atnumber or nullWhen an unclaimed stack expires. null once claimed.
expired_atnumber or nullWhen it expired.
retained_untilnumber or nullFor an expired stack, when its snapshot is deleted if nobody claims it (7 days after expiry).
generationintegerThe generation being served. Starts at 1 and increases with each successful redeploy.
deployinginteger or nullThe generation being built, while a deploy is running.
errorstring or nullWhy the stack is failed.
deploy_errorstring or nullWhy the last redeploy was rolled back. The stack stays live on the previous generation.

Timestamps are Unix seconds as floats.

Create a stack#

POST /v0/stacks

Uploads source and starts the first build. Anonymous: no Authorization header. With an API key, the stack belongs to that account from the start: no claim code, no TTL, and no anonymous rate limit or per-network cap. Any other Authorization value is rejected with 401.

The body is multipart/form-data:

FieldTypeMeaning
sourcefile, requiredA tar or tar.gz of the project. At most 50 MB. Entries that escape the root are rejected; symlinks pointing outside it and device files are dropped.
manifesttextManifest as YAML or JSON. Takes precedence over a manifest file in the archive.
manifest_filefileSame, as an upload. Takes precedence over manifest.
nametextStack name. Overrides the manifest's.
ttltextDuration such as 30m. Overrides the manifest's. Default 1h, minimum 15m.
claim_emailtextOnly this email may claim. Overrides the manifest's.

Query: wait=true holds the response until the build has finished or failed, for up to 90 seconds. A longer build comes back with status: "building": call GET /v0/stacks/{id}?wait=true until it settles. Without it, the response returns immediately with status: "building".

tar czf /tmp/src.tgz -C ./todo-app .
curl -s "$AGENTSERVE/v0/stacks?wait=true" \
  -F source=@/tmp/src.tgz \
  -F ttl=30m

Response: 201 with the stack object and these one-time fields. Store them; they are not shown again.

FieldWhenMeaning
manage_tokenalwaysManages this stack.
claim_codeanonymousCLM-XXXX-XXXX-XXXX.
claim_urlanonymous<base>/claim/<claim_code>. Give it to your human.
noteanonymousA reminder to hand over the claim URL.
webhook_secretmanifest sets notify.webhookwhsec_…, for verifying webhook signatures.

Errors: 400 no source; 401 invalid API key; 413 too large; 422 invalid manifest, archive or TTL; 429 more than 30 anonymous creates in an hour from your IP, or 3 unclaimed stacks already building or live from your IP.

Get a stack#

GET /v0/stacks/{id} · manage token or owner's API key

Query: wait=true waits for a running build or restore to settle, as above.

curl -s "$AGENTSERVE/v0/stacks/stk_3f9a1c0b2d4e" \
  -H "Authorization: Bearer $MANAGE_TOKEN"

Response: 200 with the stack object plus role: "agent" for a manage token, "owner" for an API key. Errors: 401, 403, 404.

Extend the TTL#

PATCH /v0/stacks/{id} · manage token or owner's API key

JSON body: {"ttl": "<duration>"}. Sets expires_at to now plus the duration, replacing the current expiry, and resets the stack.expiring notice.

curl -s -X PATCH "$AGENTSERVE/v0/stacks/stk_3f9a1c0b2d4e" \
  -H "Authorization: Bearer $MANAGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ttl": "1h"}'

Response: 200 with the stack object. Errors: 400 no ttl in the body; 409 the stack is claimed (no TTL) or already expired; 422 invalid duration, or an expiry past the limit for unclaimed stacks, which is counted from creation (the message gives the latest possible expiry).

Redeploy#

POST /v0/stacks/{id}/deploy · manage token or owner's API key

Multipart body with source (required), manifest and manifest_file, as for create. name, ttl and claim_email are not accepted: they are fixed at creation. Query: wait=true.

The new generation builds while the current one keeps serving. If it builds and becomes healthy, it replaces the current one. If not, the previous generation is kept (or brought back) and deploy_error is set. A failed stack can be redeployed; it goes back to building. See Redeploys.

curl -s "$AGENTSERVE/v0/stacks/stk_3f9a1c0b2d4e/deploy?wait=true" \
  -H "Authorization: Bearer $MANAGE_TOKEN" \
  -F source=@/tmp/src.tgz

Response: 200 with the stack object. If this redeploy adds notify.webhook to a stack that had none, the response also contains webhook_secret. Check deploy_error after waiting: a 200 does not mean the new generation is serving.

Errors: 409 the stack expired (claim it to restore it), or a build is already running (including the first build); 413; 422.

Delete a stack#

DELETE /v0/stacks/{id} · manage token or owner's API key

Stops the processes and deletes the source, builds, logs and $DATA_DIR of every service. The manage token and claim code stop working. This cannot be undone.

curl -s -X DELETE "$AGENTSERVE/v0/stacks/stk_3f9a1c0b2d4e" \
  -H "Authorization: Bearer $MANAGE_TOKEN"
{"id": "stk_3f9a1c0b2d4e", "status": "deleted"}

Logs#

GET /v0/stacks/{id}/logs · manage token or owner's API key

QueryDefaultMeaning
serviceallOne service.
kindrunrun (process output) or build (install and build output of the latest build).
tail200Last lines per service.
curl -s "$AGENTSERVE/v0/stacks/stk_3f9a1c0b2d4e/logs?service=api&kind=build" \
  -H "Authorization: Bearer $MANAGE_TOKEN"

Response: 200, text/plain, one section per service:

### api (build)
== build api (python-3.12) gen 2

$ uv venv -q --python 3.12 .agentserve-venv
…
== build ok

Errors: 400 kind is not run or build; 404 no such service (the message lists the stack's services).

Events#

GET /v0/stacks/{id}/events · manage token or owner's API key

Query: after (default 0), an event id. Returns up to 200 events with a greater id, oldest first. To poll, pass the last id you saw.

curl -s "$AGENTSERVE/v0/stacks/stk_3f9a1c0b2d4e/events?after=41" \
  -H "Authorization: Bearer $MANAGE_TOKEN"
{
  "events": [
    {
      "id": 42,
      "stack_id": "stk_3f9a1c0b2d4e",
      "type": "stack.live",
      "data": {"generation": 2, "urls": {"api": "https://api-todo-app-3f9a.agentserve.sh"}, "expires_at": 1791649931.2},
      "created_at": 1791646390.8
    }
  ]
}

Every event type and payload is listed in Events.

Claim a stack#

POST /v0/claim · API key

Moves a stack into the API key's account, using its claim code. The stack loses its TTL; if it had expired, it is restored at the same URLs with its data. Humans normally claim in the browser at the claim URL instead. An agent should only call this with its human's API key.

JSON body: {"claim_code": "CLM-7KQ2-M9XD-4HTW"}. The code is accepted in any case, with or without dashes and the CLM prefix.

curl -s -X POST "$AGENTSERVE/v0/claim" \
  -H "Authorization: Bearer $AGENTSERVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"claim_code": "CLM-7KQ2-M9XD-4HTW"}'

Response: 200 with the stack object (claimed: true). Errors: 401 no valid API key; 403 the stack is bound to a different claim_email; 404 code not found or already used; 429 more than 10 claim requests a minute from your IP.

Current account#

GET /v0/me · API key

curl -s "$AGENTSERVE/v0/me" -H "Authorization: Bearer $AGENTSERVE_API_KEY"
{"email": "[email protected]", "stacks": [ … stack objects, newest first … ]}

Errors: 401 invalid API key.

Health check#

GET /healthz (outside /v0, no authentication) returns {"ok": true}.

Stuck? Your agent can read /skill.md, or connect it over MCP.