# Your first run from code

Create an org key, start a session with one request, follow it live with curl, read its answer and send a follow-up into the same sandbox.

In this lesson you start an agent from a terminal with nothing but `curl`. The run needs no
repository, so you can skip GitHub for now. By the end you have one session that took two turns in
the same sandbox, and you know the four calls every integration is built from: create, stream, read
and follow up.

_Figure: request to record, on the page at /docs/learn/first-api-run._
## Get a key

### 1. Create the key

An org admin creates a key in the console under **Settings → API keys**, or from a signed-in
terminal with [`createApiKey`](/docs/api/create-api-key):

```bash
rush http POST /o/$ORG/api-keys -d '{"name": "first-run"}'
```

The response carries the secret once, in `secret`, starting `rk_live_`. Rush never shows it again.
Leave out `expires_at` for a key that never expires, or set it up to a year out.

### 2. Keep it on your server

```bash
export RUSH_API_KEY=rk_live_...
export ORG=acme
```

The key acts as the admin who created it, in its own org. Its runs use that admin's own Claude or
ChatGPT subscription first, then their Anthropic or OpenAI API key. With no account connected, a run
uses the one-time $1 of models Rush covers for a first try.

## Start a session

### 3. Send the request

A session needs a `harness` (`claude`, `codex` or `opencode`) and an `input`, the task:

```bash
curl -X POST https://api.prix.dev/o/$ORG/sessions \
  -H "Authorization: Bearer $RUSH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: first-run-1" \
  -d '{"harness": "claude", "input": "Write a Python function that parses ISO 8601 durations, with tests. Run the tests and report the result."}'
```

### 4. Read the answer

The response is `202` with the session, before the sandbox is ready:

```json
{"execution_id": "3f9c2a71d04b8e65", "org_id": "acme", "status": "allocating",
 "harness": "claude", "created_at": "2026-10-09T00:56:44.205Z"}
```

Keep `execution_id`; every other call names it. The `Idempotency-Key` header makes a retry safe: the
same key and body return the session it first started instead of starting a second one.

```bash
export SESSION=3f9c2a71d04b8e65
```

## Follow it live

### 5. Hold the event stream

```bash
curl -N https://api.prix.dev/o/$ORG/sessions/$SESSION/events \
  -H "Authorization: Bearer $RUSH_API_KEY"
```

`-N` turns off curl's buffering so each frame prints as it arrives. Each frame is an `event:` line and
a JSON `data:` line. First come `status` events while the sandbox starts, then the agent's own
events: `message`, `tool_use`, `tool_result`, `thinking` and `result`.

```text
event: status
data: {"status":"allocating","id":"3f9c2a71d04b8e65"}

event: done
data: {"status":"completed","output":"...","result":{"text":"All 9 tests pass.","structured":null,"schema_valid":null}}
```

The stream closes after `done`. If your connection drops, reconnect with the `Last-Event-ID` header
set to the last `id` you saw and the stream resumes after it. The
[`streamSession`](/docs/api/stream-session) reference lists every event.

## Read the session

### 6. Read the result

```bash
curl https://api.prix.dev/o/$ORG/sessions/$SESSION \
  -H "Authorization: Bearer $RUSH_API_KEY"
```

[`getSession`](/docs/api/get-session) returns the record. The fields to look at first:

| Field | What it holds |
| --- | --- |
| `status` | `completed`, or `paused` once the sandbox parks between turns |
| `result` | The agent's final answer for the last finished turn, in full |
| `artifacts` | Files the agent saved, each with a download `url` good for an hour |
| `turns_count`, `tool_calls_count` | How much work the run did |
| `total_cost_usd` | What the run cost so far |

A field with no value is left out of the response, never sent as `null`.

## Send a follow-up

### 7. Continue in the same sandbox

A run started with a key is resumable: when the turn ends, the sandbox parks with its disk and
memory and costs nothing while parked. A follow-up wakes it where it left off:

```bash
curl -X POST https://api.prix.dev/o/$ORG/sessions/$SESSION/messages \
  -H "Authorization: Bearer $RUSH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input": "Add support for week durations like P2W and rerun the tests."}'
```

The answer is `202` with the session, `status: "running"`. The agent still has the file it wrote in
the first turn.

### 8. Wait for the turn to end first

Send a follow-up only after a turn ends. While a turn is running, a key's follow-up is refused with
`409 TURN_ACTIVE`; it is never queued. Wait for the stream's `done` event, or for `status` to leave
`running`, then send it.

A parked sandbox is kept for 7 days after it last parked, and each new turn starts that clock again.

## Next

- [Act on the result with a webhook](/docs/learn/act-on-the-result) replaces the open stream with a
  signed `POST` to your server.
- [Runs and sessions](/docs/concepts/runs-and-sessions) covers every status a session moves through.
