> ## Documentation Index
> Fetch the complete documentation index at: https://datagen.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Conversations

> Resume, fork, list, share, and delete agent conversations

Conversation state lives on the server. You send one new user message per call and
identify the thread with `metadata.conversation_id`; the agent resumes its full
prior context — including files it wrote and what it learned — without you
replaying any history.

<Note>
  Examples use DataGen Cloud (`https://api.datagen.dev`). Self-hosted installs
  swap in their own host, e.g. `http://10.0.0.42:3001` — see
  [Base URL](/api-reference/agent-api/overview#base-url).
</Note>

***

## Resuming

<Steps>
  <Step title="Start a conversation">
    Call `/v1/messages` with no `conversation_id`. Read the
    **`X-Conversation-Id`** response header — or supply your own id up front and
    skip this step entirely.
  </Step>

  <Step title="Send the next turn with that id">
    ```bash theme={null}
    curl -N -X POST "https://api.datagen.dev/api/agents/data-agent/v1/messages" \
      -H "X-Api-Key: $DATAGEN_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "claude-haiku-4-5",
        "stream": true,
        "metadata": {"conversation_id": "support-ticket-4821"},
        "messages": [{"role": "user", "content": "Now draft a reply to the customer."}]
      }'
    ```
  </Step>
</Steps>

<Tip>
  **Bring your own id.** An id that doesn't exist yet is created with exactly the
  string you sent — it doesn't have to be a UUID. Derive it from your ticket
  number, Slack thread, or user id and you never have to store a mapping table.
</Tip>

<Warning>
  **Don't send the next turn the instant the stream ends.** A turn's stream closes
  a few seconds before the server finishes recording which session the
  conversation now points at. A message sent inside that window resumes a
  conversation with no session recorded yet, and the agent answers with no memory
  of the turn that just happened — no error, just amnesia.

  A person typing the next message never notices. An automated client does, every
  time. Before resuming a conversation you just ran a turn on, poll
  `GET /conversations/{id}` until `claude_session_id` is non-null:

  ```bash theme={null}
  until curl -s "$BASE/api/agents/$AGENT/conversations/$ID" -H "X-Api-Key: $KEY" \
        | jq -e '.conversation.claude_session_id' >/dev/null; do sleep 0.25; done
  ```

  Bound the wait and proceed anyway if it expires — a turn that produced no output
  legitimately has no session id.
</Warning>

### How an id resolves

| `conversation_id`                         | Result                                               |
| ----------------------------------------- | ---------------------------------------------------- |
| Omitted                                   | New conversation; id returned in `X-Conversation-Id` |
| Unknown id                                | Created with that id, verbatim                       |
| Yours, on this agent                      | Resumes                                              |
| Belongs to a different agent or workspace | `404 Conversation not found`                         |
| Someone else's, not shared                | `404` — existence is never leaked                    |
| Someone else's, shared with the workspace | `403` — readable, not writable. Fork it.             |

### Durability

Transcripts are stored durably, so a conversation resumes days or weeks later. The
agent's sandbox is kept warm for a while after each turn and then reclaimed;
resuming after that costs a slower first turn but loses no context.

<Note>
  If a turn ends without producing any output while resuming, the resume pointer
  is cleared so the next turn starts a clean session instead of silently returning
  nothing forever. Retrying a "silent" conversation is the right move.
</Note>

***

## Forking

Branch a conversation to explore an alternative without disturbing the original.

```bash theme={null}
curl -X POST \
  "https://api.datagen.dev/api/agents/data-agent/conversations/{parent-id}/fork" \
  -H "X-Api-Key: $DATAGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"at_turn": 2}'
```

```json theme={null}
{
  "conversation_id": "ff3feac1-7da5-48ac-91ca-0064ec9eaa96",
  "parent_conversation_id": "8bb802a6-a001-48d2-8b70-d9c01536c6bc",
  "forked_at_turn": 2,
  "claude_session_id": "6210add3-d666-4474-a6f8-45ee74d536a4"
}
```

| Body field             | Default        | Notes                                                               |
| ---------------------- | -------------- | ------------------------------------------------------------------- |
| `at_turn`              | all turns      | Copy history up to this turn. Clamped to the conversation's length. |
| `conversation_id`      | auto-generated | Mint the child with your own id. `409` if it's taken.               |
| `model` / `automation` | inherited      | Defaults recorded on the child for its future turns.                |

The child is owned by you and private by default. Its first turn answers from the
parent's context, then continues on its own branch — the parent is untouched.

***

## Managing conversations

All routes are under `/api/agents/{agent}/conversations` and use the same
authentication. You can see conversations that are yours, shared with the
workspace, or channel-owned (Slack, email).

| Method   | Path                           | Purpose                                                                   |
| -------- | ------------------------------ | ------------------------------------------------------------------------- |
| `GET`    | `/conversations`               | List, newest activity first                                               |
| `GET`    | `/conversations/{id}`          | Metadata for one conversation                                             |
| `GET`    | `/conversations/{id}/messages` | Full transcript                                                           |
| `POST`   | `/conversations/{id}`          | Share toggle — body **must** be `{"shared": true}` or `{"shared": false}` |
| `POST`   | `/conversations/{id}/fork`     | Branch (above)                                                            |
| `DELETE` | `/conversations/{id}`          | Delete. `204 No Content`                                                  |

List query parameters: `channel` (`INTERACTIVE`, `NOTEBOOK`, `SLACK`, `EMAIL`),
`channel_id`, `notebook_path`, `automation=<name>`, `since=<iso8601>`, and
`limit` (1–200, default 50).

```json theme={null}
{
  "conversations": [
    {
      "id": "support-ticket-4821",
      "title": "Summarize yesterday's failed runs",
      "channel": "INTERACTIVE",
      "shared": false,
      "is_owner": true,
      "claude_session_id": "5a3e26ed-…",
      "parent_conversation_id": null,
      "forked_at_turn_index": null,
      "created_at": "2026-07-31T16:09:28.503Z",
      "updated_at": "2026-07-31T16:09:44.930Z",
      "turn_count": 1
    }
  ]
}
```

`title` is derived from the first prompt when the conversation is created and
never re-stamped, so it always names the original question.

### Transcript

`GET /conversations/{id}/messages` returns the conversation metadata, an
Anthropic-shaped `messages[]` array, and a raw `events[]` array for callers that
want the underlying agent event stream.

```json theme={null}
{
  "conversation": { "…": "…" },
  "messages": [
    {
      "role": "user",
      "content": [{"type": "text", "text": "Summarize yesterday's failed runs"}],
      "author_user_id": "14d6cade-…",
      "author_name": "alex@example.com"
    },
    { "role": "assistant", "content": [{"type": "text", "text": "Three runs failed…"}] }
  ],
  "events": []
}
```

Long conversations are compacted automatically by the agent runtime. A compaction
appears as a `user` message flagged `is_compact_summary: true` — render it as a
"context compacted" notice rather than a user turn. `GET
/conversations/{id}/last-compaction` returns `{compacted, summary}` so a client
can tell an empty turn apart from a compaction step.

### Sharing and ownership

Conversations are private to their creator until shared. Sharing grants **read**
access to the workspace — never write: a member who opens a shared conversation
can read it and fork it, but sending a turn into it returns `403`. Only the owner
can toggle sharing or delete, with no admin override.
