# Your first agent in 10 minutes

> Create an application tool, publish an agent, and complete a tool call.

## Prepare

[Create an API key](https://pantheon-todo.alethiconsulting.com/docs/get-a-key). Install `curl` and `jq`.

```bash title="Terminal"
export PANTHEON_BASE_URL="https://pantheon-todo.alethiconsulting.com/pantheon-api"
export PANTHEON_API_KEY="PANTHEON_API_KEY"
export PANTHEON_MODEL="deepseek/deepseek-v4-flash"
export BUILD_SUFFIX="$(date +%s)"
```

Take model ids from `GET` `/v1/models`.

## Create a tool

Register an echo application tool.

```bash title="Terminal"
TOOL_JSON=$(curl -sS -X POST "$PANTHEON_BASE_URL/v1/tools" \
  -H "Authorization: Bearer $PANTHEON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "echo_tool_'"$BUILD_SUFFIX"'",
    "display_name": "Echo tool",
    "description": "Echo a message.",
    "executor": "application",
    "input_schema": {
      "type": "object",
      "properties": {"message": {"type": "string"}},
      "required": ["message"],
      "additionalProperties": false
    },
    "output_schema": {
      "type": "object",
      "properties": {"echoed": {"type": "string"}},
      "required": ["echoed"],
      "additionalProperties": false
    },
    "side_effect": "none",
    "confirmation": "never"
  }')
export TOOL_ID=$(printf '%s' "$TOOL_JSON" | jq -r '.id')
export TOOL_DEFINITION_ID=$(printf '%s' "$TOOL_JSON" | jq -r '.latest_definition.id')
```

```json title="Selected JSON response fields"
{
  "id": "tool_123",
  "name": "echo_tool",
  "display_name": "Echo tool",
  "latest_version": 1,
  "status": "active",
  "created_at": "2026-09-08T12:00:00Z",
  "updated_at": "2026-09-08T12:00:00Z",
  "latest_definition": {
    "id": "tool_definition_123",
    "tool_id": "tool_123",
    "name": "echo_tool",
    "version": 1
  }
}
```

The response is a `ToolResponse`. Its top-level `id` identifies the tool.

## Create an agent

Set `requires_sandbox` to `false`. This tool runs in your application.

```bash title="Terminal"
AGENT_JSON=$(curl -sS -X POST "$PANTHEON_BASE_URL/v1/agents" \
  -H "Authorization: Bearer $PANTHEON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Echo agent '"$BUILD_SUFFIX"'",
    "slug": "echo-agent-'"$BUILD_SUFFIX"'",
    "model": "'"$PANTHEON_MODEL"'",
    "system_prompt": "Call the echo tool when asked to echo text.",
    "requires_sandbox": false,
    "tool_requirements": [{"tool_id": "'"$TOOL_ID"'", "required": true}]
  }')
export AGENT_ID=$(printf '%s' "$AGENT_JSON" | jq -r '.id')
export AGENT_VERSION=$(printf '%s' "$AGENT_JSON" | jq -r '.version')
export AGENT_SLUG="echo-agent-$BUILD_SUFFIX"
```

## Publish a deployment

Deployments bind `latest_definition.id` through `tool_definition_ids`.

```bash title="Terminal"
DEPLOYMENT_JSON=$(curl -sS -X POST "$PANTHEON_BASE_URL/v1/deployments" \
  -H "Authorization: Bearer $PANTHEON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "'"$AGENT_ID"'",
    "agent_version": '"$AGENT_VERSION"',
    "tool_definition_ids": ["'"$TOOL_DEFINITION_ID"'"],
    "model": "'"$PANTHEON_MODEL"'"
  }')
export DEPLOYMENT_ID=$(printf '%s' "$DEPLOYMENT_JSON" | jq -r '.id')
```

## Create a session

The slug selects the active deployment, or the newest published deployment when none is active.

```bash title="Terminal"
SESSION_JSON=$(curl -sS -X POST "$PANTHEON_BASE_URL/v1/sessions" \
  -H "Authorization: Bearer $PANTHEON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent_slug": "'"$AGENT_SLUG"'", "title": "First agent"}')
export SESSION_ID=$(printf '%s' "$SESSION_JSON" | jq -r '.id')
printf '%s
' "$SESSION_JSON" | jq '{id, deployment_source, sandbox_status}'
```

```json title="Selected JSON"
{
  "id": "session_123",
  "deployment_source": "published",
  "sandbox_status": "none"
}
```

## Run, pause, and resume

Install an SDK client for streaming and tool correlation. Export `PANTHEON_AGENT_SLUG` first.

```bash title="Terminal"
export PANTHEON_AGENT_SLUG="$AGENT_SLUG"
```

**Terminal, TypeScript**

```typescript title="Terminal, TypeScript"
npm install @alethi/pantheon-chat
```

**Terminal, Python**

```bash title="Terminal, Python"
pip install pantheon-chat
```

**app.mjs**

```javascript title="app.mjs"
import { PantheonClient } from "@alethi/pantheon-chat";

const client = new PantheonClient({
  baseUrl: process.env.PANTHEON_BASE_URL,
  apiKey: process.env.PANTHEON_API_KEY,
});
const session = await client.createSession({ agentSlug: process.env.PANTHEON_AGENT_SLUG });
const run = await client.startRun(session.id, "Echo hello");

for await (const event of client.streamEvents(session.id)) {
  if (event.type === "message.delta") process.stdout.write(event.delta.text || "");
  if (event.type === "agent.custom_tool_use") {
    await client.resumeWithToolResult({
      sessionId: session.id,
      runId: run.run_id,
      inReplyToEventId: event.id,
      toolCallId: event.content.tool_call_id,
      content: { echoed: event.content.input.message },
    });
  }
  if (event.type === "run.completed") break;
}
```

**app.py**

```python title="app.py"
import asyncio
import os
from pantheon_chat import PantheonClient

async def main():
    async with PantheonClient(
        os.environ["PANTHEON_BASE_URL"],
        os.environ["PANTHEON_API_KEY"],
    ) as client:
        session = await client.create_session(agent_slug=os.environ["PANTHEON_AGENT_SLUG"])
        run = await client.start_run(session.id, "Echo hello")
        async for event in client.stream_events(session.id, run_id=run.run_id):
            if event.type == "agent.custom_tool_use":
                await client.resume_with_tool_result(
                    session.id,
                    run.run_id,
                    in_reply_to_event_id=event.sse_id,
                    content={"echoed": event.content["input"]["message"]},
                )
            if event.type == "run.completed":
                break

asyncio.run(main())
```

The stream emits live deltas when `event_deltas=true`. An application tool pauses at `agent.custom_tool_use`.

Resume with its SSE event id. Stop when `run.completed` arrives.

Read [Application tools](https://pantheon-todo.alethiconsulting.com/docs/application-tools) for approval and retry behavior.

### Raw HTTP

Use the wait endpoint when you do not need live output.

```bash title="Terminal"
RUN_JSON=$(curl -sS -X POST "$PANTHEON_BASE_URL/v1/sessions/$SESSION_ID/runs/wait" \
  -H "Authorization: Bearer $PANTHEON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"events":[{"type":"user.message","content":[{"type":"text","text":"Echo hello"}]}]}')
export RUN_ID=$(printf '%s' "$RUN_JSON" | jq -r '.run_id')
export TOOL_EVENT_ID=$(printf '%s' "$RUN_JSON" | jq -r '.required_event_ids[0]')

curl -sS -X POST "$PANTHEON_BASE_URL/v1/sessions/$SESSION_ID/runs/$RUN_ID/resume" \
  -H "Authorization: Bearer $PANTHEON_API_KEY" \
  -H "Idempotency-Key: resume-$TOOL_EVENT_ID" \
  -H "Content-Type: application/json" \
  -d '{"events":[{"type":"user.custom_tool_result","in_reply_to_event_id":"'"$TOOL_EVENT_ID"'","content":{"echoed":"hello"}}]}'
```

## Pitfalls

> **Warning**
>
> - **The first run takes a few seconds to start:** Run admission currently constructs the agent inside the request.
> - **A tool result returns 422:** Match the registered output schema. Use operation-specific output shapes in your own tool.
> - **The stream emits stream.resync:** Reconnect with the last durable SSE id and read persisted events again.
> - **The session returns SESSION_BUSY:** Resume or cancel the active run before sending another message.
