API Reference

Sandbox/Thread runtime with AI SDK compatible streaming.

Sandboxes

A sandbox is a persistent E2B environment with its own filesystem, git state, and sessions. All endpoints require Authorization: Bearer <api_key> header.

Create Sandbox

POST https://relay.an.dev/v1/sandboxes

Required: agent (slug). Optional: files, envs, setup — these override/extend the agent's deployed sandbox config.

// POST /v1/sandboxes
{
  "agent": "my-agent",
  "files": { "/home/user/config.json": "{\"key\": \"value\"}" },
  "envs": { "API_TOKEN": "sk-..." },
  "setup": ["npm install -g prettier"]
}

// Response
{
  "id": "sb_abc123",
  "sandboxId": "e2b-sandbox-id",
  "status": "active",
  "createdAt": "2026-02-26T12:00:00Z"
}

Get Sandbox

GET https://relay.an.dev/v1/sandboxes/:id
// GET /v1/sandboxes/:id

// Response
{
  "id": "sb_abc123",
  "sandboxId": "e2b-sandbox-id",
  "status": "active",
  "error": null,
  "agent": { "slug": "my-agent", "name": "My Agent" },
  "threads": [
    { "id": "th_xyz789", "name": "Chat 1", "status": "completed" }
  ],
  "createdAt": "2026-02-26T12:00:00Z",
  "updatedAt": "2026-02-26T12:05:00Z"
}

Delete Sandbox

DELETE https://relay.an.dev/v1/sandboxes/:id
// DELETE /v1/sandboxes/:id

// Response: 204 No Content

Kills the E2B sandbox and cascades deletion to all threads within it.

Sandbox Operations

Run commands, read/write files, and clone repos inside a sandbox.

Execute Command

POST https://relay.an.dev/v1/sandboxes/:id/exec
// POST /v1/sandboxes/:id/exec
{
  "command": "ls -la /home/user",
  "cwd": "/home/user/repo",
  "envs": { "NODE_ENV": "production" },
  "timeoutMs": 30000
}

// Response
{
  "stdout": "total 24\ndrwxr-xr-x 5 user user 4096 ...",
  "stderr": "",
  "exitCode": 0
}

Write Files

POST https://relay.an.dev/v1/sandboxes/:id/files
// POST /v1/sandboxes/:id/files
{
  "files": [
    { "path": "/home/user/hello.txt", "content": "Hello world!" },
    { "path": "/home/user/config.json", "content": "{\"key\": \"value\"}" }
  ]
}

// Response: 200 OK

Read File

GET https://relay.an.dev/v1/sandboxes/:id/files?path=...
// GET /v1/sandboxes/:id/files?path=/home/user/hello.txt

// Response
{
  "path": "/home/user/hello.txt",
  "content": "Hello world!"
}

Clone Repository

POST https://relay.an.dev/v1/sandboxes/:id/git/clone
// POST /v1/sandboxes/:id/git/clone
{
  "url": "https://github.com/org/repo.git",
  "path": "/home/user/repo",
  "token": "ghp_...",
  "depth": 1
}

// Response
{
  "path": "/home/user/repo",
  "branch": "default"
}

For private repos, pass a token (e.g. GitHub PAT). Use depth: 1 for faster shallow clones.

Threads

A thread is a conversation within a sandbox. Each thread has its own message history, status, and cost tracking.

List Threads

GET https://relay.an.dev/v1/sandboxes/:id/threads
// GET /v1/sandboxes/:id/threads

// Response
[
  {
    "id": "th_xyz789",
    "name": "Chat 1",
    "status": "completed",
    "createdAt": "2026-02-26T12:00:00Z",
    "updatedAt": "2026-02-26T12:05:00Z"
  }
]

Create Thread

POST https://relay.an.dev/v1/sandboxes/:id/threads
// POST /v1/sandboxes/:id/threads
{
  "name": "Chat 1"
}

// Response
{
  "id": "th_xyz789",
  "name": "Chat 1",
  "status": "active",
  "createdAt": "2026-02-26T12:00:00Z"
}

Get Thread

GET https://relay.an.dev/v1/sandboxes/:id/threads/:threadId
// GET /v1/sandboxes/:id/threads/:threadId

// Response
{
  "id": "th_xyz789",
  "name": "Chat 1",
  "status": "completed",
  "messages": [
    { "role": "user", "content": "Hello" },
    { "role": "assistant", "content": "Hi there!" }
  ],
  "createdAt": "2026-02-26T12:00:00Z",
  "updatedAt": "2026-02-26T12:05:00Z"
}

Delete Thread

DELETE https://relay.an.dev/v1/sandboxes/:id/threads/:threadId
// DELETE /v1/sandboxes/:id/threads/:threadId

// Response: 204 No Content

Chat Endpoint

POST https://relay.an.dev/v1/chat/:slug

Body `sandboxId` + `threadId`: continue an existing thread in a sandbox.

Body `sandboxId` only: creates a new thread in the existing sandbox.

No sandbox/thread: creates a new sandbox and a new thread automatically.

`threadId` without `sandboxId`: returns a 400 error.

Resuming & Cancelling

GET  https://relay.an.dev/v1/chat/:slug/:sandboxId/stream
DELETE https://relay.an.dev/v1/chat/:slug/:sandboxId/stream

Use the sandbox ID in the URL to resume or cancel an active stream. Use DELETE .../stream for deterministic server-side cancel.

Request Body

{
  "sandboxId": "uuid-of-existing-sandbox",
  "threadId": "uuid-of-existing-thread",
  "messages": [
    {
      "id": "msg-1",
      "role": "user",
      "parts": [{ "type": "text", "text": "Your message" }]
    }
  ]
}

SSE Response

data: {"type":"start"}
data: {"type":"text-start","id":"..."}
data: {"type":"text-delta","id":"...","delta":"Hello world"}
data: {"type":"text-end","id":"..."}
data: {"type":"message-metadata","messageMetadata":{
  "sessionId":"...",
  "totalCostUsd":0.034,
  "inputTokens":3,
  "outputTokens":5,
  "durationMs":2487
}}
data: {"type":"finish"}
data: [DONE]

cURL

curl -N -X POST https://relay.an.dev/v1/chat/YOUR_AGENT_SLUG \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sandboxId": "uuid-of-existing-sandbox",
    "threadId": "uuid-of-existing-thread",
    "messages": [{
      "id": "msg-1",
      "role": "user",
      "parts": [{ "type": "text", "text": "Hello" }]
    }]
  }'

React (SDK)

For the recommended approach using @an-sdk/nextjs with server-side token exchange, see the Get Started guide.

Node.js SDK

Use @an-sdk/node for server-side sandbox and thread management.

server.ts
import { AnClient } from "@an-sdk/node"

const an = new AnClient({ apiKey: process.env.AN_API_KEY! })

// Create a sandbox (with optional overrides)
const sandbox = await an.sandboxes.create({
  agent: "my-agent",
  envs: { API_TOKEN: "sk-..." },
})

// Clone a repo into the sandbox
await an.sandboxes.git.clone({
  sandboxId: sandbox.id,
  url: "https://github.com/org/repo.git",
  depth: 1,
})

// Execute a command
const result = await an.sandboxes.exec({
  sandboxId: sandbox.id,
  command: "ls /home/user/repo",
})

// Write and read files
await an.sandboxes.files.write({
  sandboxId: sandbox.id,
  files: [{ path: "/home/user/note.txt", content: "Hello!" }],
})
const file = await an.sandboxes.files.read({
  sandboxId: sandbox.id,
  path: "/home/user/note.txt",
})

// Threads & tokens
const thread = await an.threads.create({
  sandboxId: sandbox.id,
  name: "Chat 1",
})
const token = await an.tokens.create({ agent: "my-agent" })

// Cleanup
await an.sandboxes.delete(sandbox.id)

React (raw AI SDK)

Direct usage with AI SDK — useful for custom transports or non-Next.js frameworks.

agent-chat.tsx
import { Chat, DefaultChatTransport } from "ai"
import { useChat } from "@ai-sdk/react"

const chat = new Chat({
  transport: new DefaultChatTransport({
    api: "https://relay.an.dev/v1/chat/YOUR_AGENT_SLUG",
    headers: async () => ({
      Authorization: "Bearer YOUR_API_KEY",
    }),
    body: {
      sandboxId: "uuid-of-existing-sandbox",  // optional
      threadId: "uuid-of-existing-thread",     // optional
    },
  }),
})

export function AgentChat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({ chat })

  return (
    <form onSubmit={handleSubmit}>
      {messages.map((m) => <div key={m.id}>{m.content}</div>)}
      <input value={input} onChange={handleInputChange} />
    </form>
  )
}
API Reference — An Docs