Agent API

Use this when: you want to embed long-lived agents inside your own product — create agents, start sessions, send messages, and stream responses back to your users.

Don't use this when: you only want to call an LLM with one key (use the API Gateway instead — no sessions, no agents, same key). Or if you want to use agents from an MCP client (see MCP server).

Everything lives under https://api.agentsky.dev/v1. Authenticate with Authorization: Bearer ast_… (an API key from Developer → API keys or sky auth login). Management is request/response; conversation is a 202-send plus one standing SSE stream.

The model in 30 seconds

text
Agent  ← reusable spec (harness, instructions, capabilities)
  └─ Session  ← one conversation thread; holds context and history
       └─ Events  ← messages in, responses out, as a stream

An agent is what you configure once. A session is what runs. Your application sends user.message events and reads the stream until session.status_idle — the same loop every time.

Basic: end to end in three API calls

1. Create an agent

bash
curl -X POST https://api.agentsky.dev/v1/agents \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{
    "displayName": "Research Agent",
    "agentType": "hermes",
    "llm": "claude-sonnet-5"
  }'
# → { "id": "agent_…", "defaultSessionId": "sess_…", … }

2. Start a session and send the first message

bash
curl -X POST https://api.agentsky.dev/v1/sessions \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "agent_…",
    "initial_events": [
      { "type": "user.message",
        "content": [{ "type": "text", "text": "Summarize the signups from today" }] }
    ]
  }'
# → { "id": "sess_…", "status": "running", … }

3. Stream the response

bash
curl -N https://api.agentsky.dev/v1/sessions/sess_.../stream \
  -H "Authorization: Bearer ast_..."
# SSE frames until session.status_idle
# id: msg-77e0c4…
# event: agent.message
# data: {"id":"msg-…","type":"agent.message","text":"Signups today: 42…"}
# …
# event: session.status_idle
# data: {"stop_reason":{"type":"end_turn"}}

Read the stream until you see session.status_idle. The stream stays open — subsequent turns post another event and wait again.

Continue the conversation

bash
curl -X POST https://api.agentsky.dev/v1/sessions/sess_.../events \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{
    "requireIdle": true,
    "events": [
      { "type": "user.message",
        "content": [{ "type": "text", "text": "Which cohort drove the increase?" }] }
    ]
  }'

requireIdle: true rejects the POST if the session is already running — use it to prevent double-sends.

Advanced topics

Idempotency

Wrap every mutating call in an Idempotency-Key header (a UUID you generate once per logical operation). Replaying the same key returns the first response without side effects. Network timeouts are safe to retry with the same key.

bash
curl -X POST https://api.agentsky.dev/v1/sessions \
  -H "Authorization: Bearer ast_..." \
  -H "Idempotency-Key: 40a9ae2a-4ea5-4a50-987e-d8b3c99c96f0" \
  ...

Budgets and spending limits

Cap what a session may spend with a spend policy:

bash
curl -X POST https://api.agentsky.dev/v1/sessions/sess_.../policies \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Spend cap",
    "kind": "SPEND_CAP",
    "config": { "hardCapUsdMicros": 5000000, "warnUsdMicros": 4000000 }
  }'

The session stops with stop_reason.type: "budget_reached" when the hard cap is reached. GET /v1/sessions/{id} includes cumulative spend.

Interrupts

Stop a running session gracefully:

bash
curl -X POST https://api.agentsky.dev/v1/sessions/sess_.../events \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{ "events": [{ "type": "user.interrupt" }] }'

Send "mode": "save" to ask the agent to save what it has and end the turn cleanly.

Reconnect and replay

Missed stream events are kept in the event log forever. Replay from any event ID:

bash
# All events in order (same objects as the live stream; dedupe by event id)
curl "https://api.agentsky.dev/v1/sessions/sess_.../events" \
  -H "Authorization: Bearer ast_..."

# Resume stream from a known event id
curl -N "https://api.agentsky.dev/v1/sessions/sess_.../stream?after=msg-77e0c4…" \
  -H "Authorization: Bearer ast_..."

Files and workspace

Upload files and mount them into a session:

bash
# Upload
curl -X POST https://api.agentsky.dev/v1/files \
  -H "Authorization: Bearer ast_..." \
  -F "file=@report.pdf"
# → { "id": "file_…", … }

# Mount at session create or POST /sessions/{id}/resources
-d '{ "agent": "agent_…",
      "resources": [
        { "type": "file", "file_id": "file_…", "mount_path": "/data/report.pdf" }
      ] }'

GitHub repositories mount the same way ("type": "github_repository"); memory stores mount as "type": "memory_store".

Vaults and secrets

Store per-agent secrets (OAuth tokens, webhook signing keys) in the agent's vault:

bash
curl -X PUT https://api.agentsky.dev/v1/agents/agent_.../secrets/MY_TOKEN \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{ "value": "secret_…" }'

The agent sees the value as an environment variable; it never appears in API responses.

Webhooks

Subscribe to session events server-to-server:

bash
curl -X POST https://api.agentsky.dev/v1/webhooks \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/agentsky",
    "events": ["session.status_idle", "session.error"]
  }'

The routine_run.failed and routine.paused events are also available. Wire hook rules to call a URL or require human approval on specific tool uses (POST /hook_rules).

Environments (universes)

Create an isolated workspace for a customer, team, or environment:

bash
curl -X POST https://api.agentsky.dev/v1/universes \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{ "slug": "acme", "name": "Acme" }'

# All subsequent calls scoped to that workspace:
curl ... -H "X-Universe: acme"

Invite members (POST /universes/{slug}/invites), set policies, and pin agent versions (PUT /universes/{slug}/agents/{handle}).

Skills and agent.toml

Attach reusable instruction bundles to any agent with sky launch and an agent.toml file, or via PUT /agents/{id}/prompt. Skills are versioned bundles you upload once and attach by reference.

Agent identity fields (D-391)

Three optional fields let you give an agent a persistent identity within a workspace.

handle — workspace-scoped @-handle (lower-case, 2–40 chars: [a-z0-9_]). Unique within the workspace. Setting it gives the agent a stable email address and lets you start sessions with "agent": "@handle" instead of the agent id.

bash
# Set a handle at create time
curl -X POST https://api.agentsky.dev/v1/agents \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{
    "displayName": "Support Bot",
    "handle": "support_bot"
  }'
# → { "id": "agent_…", "handle": "support_bot",
#      "email": "support_bot@agents.agentsky.dev", … }

# Or set/update it via PATCH
curl -X PATCH https://api.agentsky.dev/v1/agents/agent_... \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{ "handle": "support_bot" }'

# Start a session by handle
curl -X POST https://api.agentsky.dev/v1/sessions \
  -H "Authorization: Bearer ast_..." \
  -H "Content-Type: application/json" \
  -d '{ "agent": "@support_bot", "initial_events": [] }'

Pass "handle": null in PATCH to clear the handle.

email — read-only. Present in agent responses when a handle is set; value is {handle}@agents.agentsky.dev. The address is reserved, not live: agents.agentsky.dev has no mail receiving yet, so mail sent to it bounces, and agents have no tool to send from it.

channel_settings — Slack/Lark persona settings. Controls whether the agent is invitable to channels and carries an optional description.

json
{ "invitable": true, "description": "Handles tier-1 support tickets" }

Pass "channel_settings": null to reset to defaults.

runtime — BYO-code runtime config. Two shapes:

  • { "kind": "image", "image": "ghcr.io/your-org/agent@sha256:<64 hex>" } — run your own container image. The reference must be digest-pinned and readable anonymously from its registry (checked on create/PATCH, 422 invalid_runtime otherwise). The agent becomes a workspace_acp agent on a hosted harness built from the image; sessions answer 409 harness_not_ready until the image's template is built. The image must run the hosted-brain contract.
  • { "kind": "endpoint", "protocol": "a2a", "url": "https://…" } — AgentSky sends each user turn to your A2A agent as message/send (no sandbox, nothing metered) and shows its reply as the agent's turn. Set the agent secret A2A_AUTH_TOKEN to have it sent as Authorization: Bearer …. acp and mcp are refused with 422 runtime_protocol_not_supported.

Note: BYO-code runtimes are enabled per workspace. Where they are not, session create answers 422 runtime_not_available.

Pass "runtime": null to clear (agent runs in the standard sandbox).

Reference

  • Full API reference — every /v1 endpoint, schemas, the error contract, and the complete stream event catalog
  • Authentication — tokens, scopes, X-Universe header
  • Concepts — agents, sessions, events, and billing explained
  • CLI — sky launch, sky agent, sky session and every other command
  • agent.toml — declarative agent configuration