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:
| Token | Prefix | Issued | Grants |
|---|---|---|---|
| Manage token | ast_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 key | as_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'"}
| Status | Meaning |
|---|---|
400 | Missing or invalid parameter. |
401 | Missing Authorization header, or an invalid API key. |
403 | The token doesn't grant access to this stack, or the claim is restricted to another email. |
404 | Stack, service or claim code not found. Deleted stacks are not found. |
409 | The stack's state doesn't allow the operation (expired, claimed, build already running). |
413 | The upload is larger than 50 MB. |
422 | Invalid manifest, archive or TTL. See validation errors. |
429 | Rate 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
}
| Field | Type | Meaning |
|---|---|---|
id | string | Stack id, stk_ plus 12 hex characters. |
name | string | Stack name. |
status | string | building, live, failed or
expired. (deleted stacks are not returned.) |
claimed | boolean | Whether the stack belongs to an account. |
urls | object | Service name to public URL. |
services | array | name, runtime, status
(pending, running, restarting or stopped) and url
of each service. |
created_at | number | Unix time, in seconds. |
expires_at | number or null | When an unclaimed stack expires. null
once claimed. |
expired_at | number or null | When it expired. |
retained_until | number or null | For an expired stack, when its snapshot is deleted if nobody claims it (7 days after expiry). |
generation | integer | The generation being served. Starts at 1 and increases with each successful redeploy. |
deploying | integer or null | The generation being built, while a deploy is running. |
error | string or null | Why the stack is failed. |
deploy_error | string or null | Why 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:
| Field | Type | Meaning |
|---|---|---|
source | file, required | A 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. |
manifest | text | Manifest as YAML or JSON. Takes precedence over a manifest file in the archive. |
manifest_file | file | Same, as an upload. Takes precedence over
manifest. |
name | text | Stack name. Overrides the manifest's. |
ttl | text | Duration such as 30m. Overrides the manifest's. Default
1h, minimum 15m. |
claim_email | text | Only 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.
| Field | When | Meaning |
|---|---|---|
manage_token | always | Manages this stack. |
claim_code | anonymous | CLM-XXXX-XXXX-XXXX. |
claim_url | anonymous | <base>/claim/<claim_code>. Give it
to your human. |
note | anonymous | A reminder to hand over the claim URL. |
webhook_secret | manifest sets notify.webhook | whsec_…,
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
| Query | Default | Meaning |
|---|---|---|
service | all | One service. |
kind | run | run (process output) or build
(install and build output of the latest build). |
tail | 200 | Last 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}.