AgentSky Developer API

The complete third-party integration guide — endpoints, authentication, and the event model.

Base URL:

text
https://agentsky.dev

The authoritative, machine-readable contract for everything below is the OpenAPI document at:

text
https://agentsky.dev/api/v1/openapi.json

If a field or endpoint is not described here, the OpenAPI document is the source of truth — not this prose.

Core model

AgentSky has a small, fixed object model:

ObjectWhat it isLifecycle
UniverseAn isolated tenancy: its own agents, sessions, secrets, and model subscriptions.Created once; everything else lives inside one.
AgentA reusable configuration: engine (agentType), model (llm), prompt, capabilities, secrets, install steps.Created, patched, archived, or deleted. Runs zero or more sessions.
SessionOne running conversation with a working directory. The pod materializes lazily on the first turn.provisioningidle / runningterminated.
TurnOne user message plus the agent's response.Started by POST /sessions/{id}/messages; boundaries read from the stream.

Nothing is provisioned when you create an agent — the pod and its working directory exist only after you start a session and send a turn.

Authentication

Every request is authorized with a bearer token that carries an ast_ prefix:

http
Authorization: Bearer ast_...

Tokens are scoped to a single universe and carry one or more scopes. Each endpoint in the OpenAPI document declares the scope it requires: read for reads, write for writes, admin for destructive or universe-level actions (for example deleting an agent requires admin).

Scopes are hierarchical — read ⊂ write ⊂ admin — so a token that can admin also implies write and read. You mint a token in the AgentSky console (Settings → API tokens) or from the sky CLI with sky auth login.

Send GET /api/v1/whoami first — it returns the resolved identity, universe, and effective scopes for the token you hold, and is the cheapest way to confirm a credential before building on it:

bash
curl -sS https://agentsky.dev/api/v1/whoami \
  -H "Authorization: Bearer $AGENTSKY_TOKEN"
json
{
  "user":    { "id": "u_...", "email": "you@example.com", "name": "You" },
  "universe": { "slug": "acme", "name": "Acme", "isPersonal": false },
  "scopes":  ["read", "write", "admin"],
  "auth":    "token"
}

Notes:

  • universe.isPersonal is true for a personal token. Only personal tokens can create universes (POST /api/v1/universes).
  • Secret values and subscription credentials are write-only: the API accepts them but never returns them.
  • Rate limit: 120 requests per minute per token; exceed it and you get HTTP 429 with a Retry-After header.
  • The human-readable documentation lives at https://agentsky.dev/docs.

Agents

Create an agent

POST /api/v1/agents — every field is optional; an empty body {} creates a default hermes agent. agentType selects the engine:

hermes (default) · claude_code · codex · openclaw · pi · dsh · kimi_code · opencode

bash
curl -sS https://agentsky.dev/api/v1/agents \
  -H "Authorization: Bearer $AGENTSKY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "support-bot",
    "displayName": "Support Bot",
    "agentType": "hermes",
    "llm": "claude-fable-5",
    "prompt": "You are a concise support assistant.",
    "capabilities": ["exa.search", "gptimage.generate"]
  }'

The response returns the full agent object including its generated slug (a globally unique handle) and version (bumped on every update). You address the agent by slug from here on.

Full CreateAgent fields:

FieldMeaning
name / displayNameInternal name (≤60 chars) and human-facing label (≤60 chars).
description≤500 chars.
agentTypeEngine, one of the eight values above.
llmModel id, passed through to the engine; engine↔model mismatches are rejected rather than silently replaced.
promptThe user prompt layer (≤100000 chars).
capabilitiesBuilt-in tool grants — see the table below.
instructionsArray of {name: "*.md", content} markdown files injected at startup.
skillsArray of {name, url, source, version, config?, secretRefs?} packages loaded into the agent.
customInstallsArray of {command, description?, timeoutSeconds? (≤1800), allowNetwork?} shell steps run before the engine starts.
customDataArray of {id, name, kind, scope?, description?, uri?, config?} data attachments.
metadataYour key-merged client metadata, echoed back on reads.

Capabilities are not LLM tools — they are platform grants surfaced through the in-pod actl CLI. The full enum:

exa.search · exa.contents · tinyfish.fetch · tinyfish.browser · dataforseo.serp · gptimage.generate · rembg.remove-background · seedance.generate · mm.i2v · fish-audio.transcribe

Read, update, archive, delete

bash
GET    /api/v1/agents                # list agents in the resolved universe
GET    /api/v1/agents/{slug}         # agent detail (includes prompt, capabilities, metadata)
PATCH  /api/v1/agents/{slug}         # update displayName / capabilities / metadata / harnessVersion
POST   /api/v1/agents/{slug}/archive # archive: read-only, sessions keep running, new sessions rejected
DELETE /api/v1/agents/{slug}         # delete — only with zero sessions (409 agent_has_sessions otherwise)

PATCH supports displayName, capabilities, metadata, harnessVersion, and expectedVersion (an optimistic-concurrency guard: pass the version you last read, and the write fails with version_conflict if the agent has moved on).

Prompt

bash
PUT /api/v1/agents/{slug}/prompt          # save + hot-reload the user prompt layer
GET /api/v1/agents/{slug}/prompt/versions # version history

PUT returns {version, unchanged, applied, sessions[]}. applied is live, on-restart, or unreachable — it tells you, per session, whether the new prompt hot-reloaded or will apply on the next restart.

Secrets

bash
GET    /api/v1/agents/{slug}/secrets        # declared secret keys + descriptions — never values
PUT    /api/v1/agents/{slug}/secrets/{key}  # set a declared secret's value (write-only)
DELETE /api/v1/agents/{slug}/secrets/{key}  # unset a secret

Secrets are injected into the agent process environment. Values are write-only: you can set and unset them, but the API never echoes them back.

Model subscriptions

A connected consumer AI subscription (Claude Pro/Max or a ChatGPT plan) can power eligible agents at $0 model usage.

bash
GET    /api/v1/model-subscriptions               # list — metadata only, never credentials
PUT    /api/v1/model-subscriptions/{provider}    # connect/replace a credential (provider: anthropic | openai)
PATCH  /api/v1/model-subscriptions/{provider}    # update useForNewAgents / label
DELETE /api/v1/model-subscriptions/{provider}    # disconnect — affected agents fail until reconnected or switched

A session opts in to account billing by setting modelBilling: "account" (see Sessions below); the platform meters the turn otherwise.

Sessions

Create a session

POST /api/v1/sessions — cheap and lazy. Only agent is required; the pod materializes on the first turn, so creation is effectively free.

bash
curl -sS https://agentsky.dev/api/v1/sessions \
  -H "Authorization: Bearer $AGENTSKY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agent": "support-bot", "title": "First session"}'

A non-empty initial_events list (user.message only, all-or-nothing, ≤50) starts the first turn in the same call:

bash
curl -sS https://agentsky.dev/api/v1/sessions \
  -H "Authorization: Bearer $AGENTSKY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "support-bot",
    "initial_events": [
      { "type": "user.message", "parts": [ { "index": 0, "type": "text", "text": "Hello" } ] }
    ]
  }'

CreateSession fields: agent (required), eager, title (≤120 chars), metadata, instructions (≤20 markdown files), initial_events, vcpus, memoryMb, and modelBilling (platform | account). Machine shape (vcpus / memoryMb) is per-session.

Read, update, delete

bash
GET    /api/v1/sessions          # list sessions; ?agent= filters to one spec
GET    /api/v1/sessions/{id}     # detail — status is provisioning | idle | running | terminated
PATCH  /api/v1/sessions/{id}     # update title / metadata / modelBilling
DELETE /api/v1/sessions/{id}     # end the session — tears down the pod, emits session.deleted

The session has a persistent working directory: files you write in one turn survive to the next. That directory is destroyed when the session is deleted.

Sending a turn

POST /api/v1/sessions/{id}/messages accepts a message and returns 202 with an empty body — the result never comes back in this response. Output arrives on the event stream (next section).

bash
curl -sS -X POST https://agentsky.dev/api/v1/sessions/$SESSION_ID/messages \
  -H "Authorization: Bearer $AGENTSKY_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: turn-7f3a" \
  -d '{
    "parts": [ { "index": 0, "type": "text", "text": "Summarize this quarter." } ]
  }'

The request body requires parts (≥1). The optional Idempotency-Key header dedupes retries — reuse the same key and a repeated send does not create a second turn.

A turn is bounded by two turn.status_idle events on the stream: one at the start (or from history) and one carrying the final stop_reason. The stop_reason.type is end_turn when the turn finished normally, or interrupted when it was aborted.

Interrupt

POST /api/v1/sessions/{id}/interrupt aborts the in-flight turn:

json
{ "status": "interrupting" }

status is interrupting when a turn is aborted, or no_turn when nothing was in flight. The stream then emits turn.interrupted.

Logs

GET /api/v1/sessions/{id}/logs returns pod logs ({lines: [{at, severity, line}]}). Pass follow=true to stream them as SSE.

Events

AgentSky has no HTTP webhooks. The event model is an SSE stream plus a pollable event history — the same event objects appear in both, so you can implement either a long-lived streaming client or a polling client on one contract.

The stream

GET /api/v1/sessions/{id}/stream is text/event-stream, live-only, and stays open across turns. Event types:

EventMeaning
user.messageA user message. History only — not sent on the live stream.
agent.messageA completed agent message (messageId, parts, text).
agent.reasoningA reasoning delta (part).
agent.tool_useA tool invocation (part with call_id, tool_name, args).
agent.tool_resultA tool result (part with call_id, tool_name, status, result).
agent.statusA status update (part with level: thinking/working/waiting/idle/done).
turn.status_idleA turn boundary. stop_reason.type is end_turn or interrupted. Never break on a bare idle — read the stop_reason.
turn.interruptedThe in-flight turn was aborted.
session.deletedTerminal — the session is gone. Close your client.
errorAn error (code, message, retryable).

The history

GET /api/v1/sessions/{id}/events returns the session history, oldest-first, with an opaque numeric cursor. Only user.message, agent.message, and turn.status_idle are persisted; the live-only types (agent.reasoning, agent.tool_use, agent.tool_result, agent.status, error) appear on the stream but not in history.

bash
curl -sS "https://agentsky.dev/api/v1/sessions/$SESSION_ID/events?limit=100" \
  -H "Authorization: Bearer $AGENTSKY_TOKEN"
json
{
  "events": [ { "id": "evt_...", "type": "agent.message", "sessionId": "sess_...", "parts": [...] } ],
  "cursor": "evt_...",
  "hasMore": false
}

Query parameters: cursor (resume), limit (≤500), and types[] filters — types[]=user.message&types[]=agent.message is the transcript view.

Reconnect and dedupe

Every event carries a stable id. The reconnect contract is:

  1. Reopen the stream.
  2. List events (GET .../events) from where you left off.
  3. Dedupe by id — an event you already processed is safe to ignore.

The same id is the dedupe key across the stream and the history, so a streaming client and a polling fallback can share the same state.

Message parts

A message is a sequence of typed parts. Every part has an integer index and a type:

typeRequired extra fields
texttext
reasoningtext, redacted
tool_callcall_id, tool_name, args, args_partial
tool_resultcall_id, tool_name, status (ok/error), result
filename, media_type, uri/data, size_bytes
imagemedia_type, uri/data, alt, width, height
videomedia_type, uri/data, alt, width, height, duration_ms, size_bytes, thumbnail_uri
statuslevel (thinking/working/waiting/idle/done), text
errorcode, message, retryable

Media parts carry either a uri (a signed URL) or inline data, plus metadata. Indexing is zero-based and defines display order.

Errors

Every error, on every endpoint, has one shape:

json
{ "error": { "code": "...", "message": "..." } }

The HTTP status distinguishes the class; code is the stable, machine-readable value. Common ones:

StatuscodeMeaning
401invalid_tokenMissing, malformed, or unknown token.
402insufficient_creditsThe universe wallet cannot pay for the turn.
409agent_has_sessionsDeleting an agent that still has sessions.
409version_conflictAn optimistic expectedVersion no longer matches the agent.
422invalid_specThe payload is well-formed but invalid (for example an engine↔model mismatch).

The OpenAPI document names agent_has_sessions and version_conflict explicitly; the remaining codes above are returned at runtime under the same { error: { code, message } } shape. Treat retryable: true on an error event as safe to retry; a bare error without that flag should be surfaced to a human.

End-to-end example

A complete integration in five calls:

bash
BASE=https://agentsky.dev
AUTH="Authorization: Bearer $AGENTSKY_TOKEN"

# 1. Confirm identity and scopes.
curl -sS $BASE/api/v1/whoami -H "$AUTH"

# 2. Create an agent.
curl -sS $BASE/api/v1/agents -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"name":"demo","agentType":"hermes","prompt":"Be concise."}'

# 3. Create a session and start the first turn in one call.
curl -sS $BASE/api/v1/sessions -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"agent":"demo","initial_events":[{"type":"user.message","parts":[{"index":0,"type":"text","text":"Say hello."}]}]}'

# 4. Open the event stream and read until turn.status_idle with stop_reason.type = "end_turn".
curl -sS -N $BASE/api/v1/sessions/$SESSION_ID/stream -H "$AUTH"

# 5. Tear down.
curl -sS -X DELETE $BASE/api/v1/sessions/$SESSION_ID -H "$AUTH"

Verification checklist

To confirm your integration before shipping, walk these in order and check the result of each:

  1. GET /whoami returns your universe and scopes without error.
  2. POST /agents returns a 201 with an agent.slug and version.
  3. GET /agents/{slug} returns the configuration you created.
  4. POST /sessions returns a 201 with a session.id and status.
  5. POST /sessions/{id}/messages returns 202 with an empty body.
  6. GET /sessions/{id}/stream delivers agent.message and a terminal turn.status_idle whose stop_reason.type is end_turn.
  7. GET /sessions/{id}/events returns the same events, dedupable by id.
  8. POST /sessions/{id}/interrupt returns interrupting during a turn.
  9. DELETE /sessions/{id} returns success and the stream emits session.deleted.

Endpoints at a glance

text
GET    /api/v1/whoami
GET    /api/v1/universes
POST   /api/v1/universes
GET    /api/v1/universes/{slug}
GET    /api/v1/agents
POST   /api/v1/agents
GET    /api/v1/agents/{slug}
PATCH  /api/v1/agents/{slug}
DELETE /api/v1/agents/{slug}
POST   /api/v1/agents/{slug}/archive
PUT    /api/v1/agents/{slug}/prompt
GET    /api/v1/agents/{slug}/prompt/versions
GET    /api/v1/agents/{slug}/secrets
PUT    /api/v1/agents/{slug}/secrets/{key}
DELETE /api/v1/agents/{slug}/secrets/{key}
GET    /api/v1/model-subscriptions
PUT    /api/v1/model-subscriptions/{provider}
PATCH  /api/v1/model-subscriptions/{provider}
DELETE /api/v1/model-subscriptions/{provider}
GET    /api/v1/sessions
POST   /api/v1/sessions
GET    /api/v1/sessions/{id}
PATCH  /api/v1/sessions/{id}
DELETE /api/v1/sessions/{id}
POST   /api/v1/sessions/{id}/messages
GET    /api/v1/sessions/{id}/events
GET    /api/v1/sessions/{id}/stream
POST   /api/v1/sessions/{id}/interrupt
GET    /api/v1/sessions/{id}/logs
Was this article helpful?