# Conversations: Scarleta API reference

Page: https://www.scarleta.ai/docs/reference/conversations
Base URL: https://api.scarleta.ai. Authentication: `Authorization: Bearer $SCARLETA_API_KEY`.
Operations: 9.

## GET /v1/conversations

Operation id: `list_conversations`

**List conversations**

Returns your most recent conversations, newest activity first. Archived (deleted) conversations are not listed. `meta.count` is the number of conversations returned.

Access: API key required; free and paid accounts. Cost: no token cost.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | integer | no | Conversations to return. Defaults to 20; values above 100 are clamped to 100, and a value that is not a positive number falls back to the default. Allowed: 1 to 100. |

### Response fields (inside `data`)

| Field | Type | Description |
| --- | --- | --- |
| `[].id` | string | Identifier of the conversation. |
| `[].audio_url` | string | The audio file the conversation is about; an empty string when none was attached. |
| `[].playback_url` | string \| null | A playable link for the audio; empty (or null) when no audio was attached. |
| `[].title` | string \| null | The conversation title. |
| `[].message_count` | integer | Messages in the conversation. Allowed: -9007199254740991 to 9007199254740991. |
| `[].status` | string | The conversation state; `active` for a conversation in use. |
| `[].created_at` | string | ISO 8601 timestamp. |
| `[].updated_at` | string | ISO 8601 timestamp. |
| `[].last_message_at` | string \| null | ISO 8601 timestamp of the newest message, or null when there is none. |
| `[].is_unread` | boolean | True when the conversation has an assistant reply you have not marked read. |

### Responses

200: `meta.request_id` is present.

```json
{
  "data": [
    {
      "id": "3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01",
      "audio_url": "",
      "playback_url": "",
      "title": "Mix review",
      "message_count": 2,
      "status": "active",
      "created_at": "2026-10-21T03:41:37.272Z",
      "updated_at": "2026-10-21T03:41:56.630Z",
      "last_message_at": "2026-10-21T03:41:56.630Z",
      "is_unread": false
    }
  ],
  "meta": {
    "request_id": "0cf07e8d-6f86-47b3-8a35-35499a651571",
    "count": 1
  }
}
```

401: Missing, malformed, unknown or revoked API key.

```json
{
  "error": {
    "code": "invalid_key",
    "message": "Invalid or revoked API key",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_key"
  }
}
```

429: Over the account request budget for the hour. Wait for the `Retry-After` header, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rate_limit_exceeded"
  }
}
```

503: Temporary platform condition; retry shortly.

```json
{
  "error": {
    "code": "auth_unavailable",
    "message": "Authentication service temporarily unavailable",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#auth_unavailable"
  }
}
```

### Error codes

[`auth_unavailable`](https://www.scarleta.ai/docs/errors#auth_unavailable), [`invalid_auth_format`](https://www.scarleta.ai/docs/errors#invalid_auth_format), [`invalid_key`](https://www.scarleta.ai/docs/errors#invalid_key), [`invalid_key_format`](https://www.scarleta.ai/docs/errors#invalid_key_format), [`list_failed`](https://www.scarleta.ai/docs/errors#list_failed), [`missing_api_key`](https://www.scarleta.ai/docs/errors#missing_api_key), [`missing_auth`](https://www.scarleta.ai/docs/errors#missing_auth), [`rate_limit_exceeded`](https://www.scarleta.ai/docs/errors#rate_limit_exceeded), [`rate_limit_unavailable`](https://www.scarleta.ai/docs/errors#rate_limit_unavailable), [`unauthorized`](https://www.scarleta.ai/docs/errors#unauthorized)

### Example

```bash
curl "https://api.scarleta.ai/v1/conversations" \
  -H "Authorization: Bearer $SCARLETA_API_KEY"
```

## POST /v1/conversations

Operation id: `create_conversation`

**Create a conversation**

Creates an empty conversation about one audio file and returns it with `201`. You do not need this call to chat: `POST /v1/chat` creates a conversation by itself when you leave out `conversation_id`. Use this route to prepare a conversation before the first message. `audio_url` must be a public `http` or `https` URL, or a file in your own library.

Access: API key required; free and paid accounts. Cost: no token cost.

### Request body

| Field | Type | Description |
| --- | --- | --- |
| `audio_url` | string | The audio file the conversation is about, at most 4096 characters. Allowed: 1 to 4096 characters. |
| `title` | string | A title, at most 200 characters. Derived from the first message when absent. |

### Response fields (inside `data`)

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Identifier of the conversation. |
| `audio_url` | string | The audio file the conversation is about; an empty string when none was attached. |
| `playback_url` | string \| null | A playable link for the audio; empty (or null) when no audio was attached. |
| `title` | string \| null | The conversation title. |
| `message_count` | integer | Messages in the conversation. Allowed: -9007199254740991 to 9007199254740991. |
| `status` | string | The conversation state; `active` for a conversation in use. |
| `created_at` | string | ISO 8601 timestamp. |

### Responses

201: Created. `meta.request_id` is present.

```json
{
  "data": {
    "id": "3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01",
    "audio_url": "https://example.com/track-1.wav",
    "playback_url": "https://example.com/track-1.wav",
    "title": "Mix review",
    "message_count": 0,
    "status": "active",
    "created_at": "2026-10-21T03:41:37.272Z"
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
```

400: Invalid JSON, a missing `audio_url`, an unacceptable URL, or a title over the limit.

```json
{
  "error": {
    "code": "missing_audio_url",
    "message": "audio_url is required",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#missing_audio_url"
  }
}
```

401: Missing, malformed, unknown or revoked API key.

```json
{
  "error": {
    "code": "invalid_key",
    "message": "Invalid or revoked API key",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_key"
  }
}
```

403: `audio_url` points at a library file owned by another account.

```json
{
  "error": {
    "code": "media_not_owned",
    "message": "You don't have access to this audio.",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#media_not_owned"
  }
}
```

429: Over the account request budget for the hour. Wait for the `Retry-After` header, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rate_limit_exceeded"
  }
}
```

503: Temporary platform condition; retry shortly.

```json
{
  "error": {
    "code": "auth_unavailable",
    "message": "Authentication service temporarily unavailable",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#auth_unavailable"
  }
}
```

### Error codes

[`auth_unavailable`](https://www.scarleta.ai/docs/errors#auth_unavailable), [`create_failed`](https://www.scarleta.ai/docs/errors#create_failed), [`invalid_audio_url`](https://www.scarleta.ai/docs/errors#invalid_audio_url), [`invalid_auth_format`](https://www.scarleta.ai/docs/errors#invalid_auth_format), [`invalid_json`](https://www.scarleta.ai/docs/errors#invalid_json), [`invalid_key`](https://www.scarleta.ai/docs/errors#invalid_key), [`invalid_key_format`](https://www.scarleta.ai/docs/errors#invalid_key_format), [`invalid_title`](https://www.scarleta.ai/docs/errors#invalid_title), [`media_not_owned`](https://www.scarleta.ai/docs/errors#media_not_owned), [`missing_api_key`](https://www.scarleta.ai/docs/errors#missing_api_key), [`missing_audio_url`](https://www.scarleta.ai/docs/errors#missing_audio_url), [`missing_auth`](https://www.scarleta.ai/docs/errors#missing_auth), [`rate_limit_exceeded`](https://www.scarleta.ai/docs/errors#rate_limit_exceeded), [`rate_limit_unavailable`](https://www.scarleta.ai/docs/errors#rate_limit_unavailable), [`title_too_long`](https://www.scarleta.ai/docs/errors#title_too_long), [`unauthorized`](https://www.scarleta.ai/docs/errors#unauthorized)

### Example

```bash
curl -X POST "https://api.scarleta.ai/v1/conversations" \
  -H "Authorization: Bearer $SCARLETA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "audio_url": "https://example.com/track-1.wav",
    "title": "Mix review"
  }'
```

## GET /v1/conversations/{conversation_id}

Operation id: `get_conversation`

**Get a conversation**

Returns one conversation with its messages, oldest first. A customer key sees the newest 50 messages: when the conversation holds more, `truncated` is true and `total_available` says how many exist. Each message carries its text and role only. Internal reasoning, tool calls, tool results and model details are never returned. To read the assistant's reply to a chat message, poll `GET /v1/conversations/{conversation_id}/status` until no turn is active, then call this route.

Access: API key required; free and paid accounts. Cost: no token cost.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `conversation_id` | path | string | yes | The conversation id, for example `3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01`. Allowed: at least 1 character. |

### Response fields (inside `data`)

| Field | Type | Description |
| --- | --- | --- |
| `truncated` | boolean | True when older messages were left out. |
| `returned_count` | integer | Messages in `messages`. Allowed: -9007199254740991 to 9007199254740991. |
| `total_available` | integer | Messages the conversation holds. Allowed: -9007199254740991 to 9007199254740991. |
| `running_recipe_jobs` | array<object> | Recipe runs of this conversation that are still queued or in progress. Empty when none. |
| `running_recipe_jobs[].job_id` | string | The running recipe job. |
| `running_recipe_jobs[].recipe_id` | string | The recipe the job runs. |
| `id` | string | Identifier of the conversation. |
| `audio_url` | string | The audio file the conversation is about; an empty string when none was attached. |
| `playback_url` | string \| null | A playable link for the audio; empty (or null) when no audio was attached. |
| `title` | string \| null | The conversation title. |
| `message_count` | integer | Messages in the conversation. Allowed: -9007199254740991 to 9007199254740991. |
| `status` | string | The conversation state; `active` for a conversation in use. |
| `created_at` | string | ISO 8601 timestamp. |
| `updated_at` | string | ISO 8601 timestamp. |
| `last_message_at` | string \| null | ISO 8601 timestamp of the newest message, or null when there is none. |
| `messages` | array<object> |   |
| `messages[].id` | string |   |
| `messages[].role` | "user" \| "assistant" | Who wrote the message. |
| `messages[].content` | string | The message text. |
| `messages[].job_id` | string \| null | The analysis job the message belongs to, when there is one. |
| `messages[].created_at` | string | ISO 8601 timestamp. |
| `messages[].turn_index` | integer \| null | Which exchange of the conversation the message belongs to, from 1. Allowed: -9007199254740991 to 9007199254740991. |
| `messages[].message_index` | integer \| null | Position of the message inside its exchange, from 0. Allowed: -9007199254740991 to 9007199254740991. |

### Responses

200: `meta.request_id` is present.

```json
{
  "data": {
    "running_recipe_jobs": [],
    "truncated": false,
    "returned_count": 2,
    "total_available": 2,
    "id": "3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01",
    "audio_url": "",
    "playback_url": "",
    "title": "Mix review",
    "message_count": 2,
    "status": "active",
    "created_at": "2026-10-21T03:41:37.272Z",
    "updated_at": "2026-10-21T03:41:56.630Z",
    "last_message_at": "2026-10-21T03:41:56.630Z",
    "messages": [
      {
        "id": "45190c94-a86b-4d7e-90da-281f0442864e",
        "role": "user",
        "content": "What key is this track in?",
        "job_id": null,
        "created_at": "2026-10-21T03:41:37.272Z",
        "turn_index": 1,
        "message_index": 0
      },
      {
        "id": "425a18c9-5206-44a0-9d7b-e43941726f76",
        "role": "assistant",
        "content": "It sits in A minor.",
        "job_id": null,
        "created_at": "2026-10-21T03:41:56.630Z",
        "turn_index": 1,
        "message_index": 1
      }
    ]
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
```

401: Missing, malformed, unknown or revoked API key.

```json
{
  "error": {
    "code": "invalid_key",
    "message": "Invalid or revoked API key",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_key"
  }
}
```

404: No conversation with this id exists for your account.

```json
{
  "error": {
    "code": "not_found",
    "message": "Conversation 3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01 not found",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#not_found"
  }
}
```

429: Over the account request budget for the hour. Wait for the `Retry-After` header, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rate_limit_exceeded"
  }
}
```

503: Temporary platform condition; retry shortly.

```json
{
  "error": {
    "code": "auth_unavailable",
    "message": "Authentication service temporarily unavailable",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#auth_unavailable"
  }
}
```

### Error codes

[`auth_unavailable`](https://www.scarleta.ai/docs/errors#auth_unavailable), [`get_failed`](https://www.scarleta.ai/docs/errors#get_failed), [`invalid_auth_format`](https://www.scarleta.ai/docs/errors#invalid_auth_format), [`invalid_key`](https://www.scarleta.ai/docs/errors#invalid_key), [`invalid_key_format`](https://www.scarleta.ai/docs/errors#invalid_key_format), [`missing_api_key`](https://www.scarleta.ai/docs/errors#missing_api_key), [`missing_auth`](https://www.scarleta.ai/docs/errors#missing_auth), [`missing_conversation_id`](https://www.scarleta.ai/docs/errors#missing_conversation_id), [`not_found`](https://www.scarleta.ai/docs/errors#not_found), [`rate_limit_exceeded`](https://www.scarleta.ai/docs/errors#rate_limit_exceeded), [`rate_limit_unavailable`](https://www.scarleta.ai/docs/errors#rate_limit_unavailable), [`unauthorized`](https://www.scarleta.ai/docs/errors#unauthorized)

### Example

```bash
CONVERSATION_ID="YOUR_CONVERSATION_ID"
curl "https://api.scarleta.ai/v1/conversations/$CONVERSATION_ID" \
  -H "Authorization: Bearer $SCARLETA_API_KEY"
```

## DELETE /v1/conversations/{conversation_id}

Operation id: `delete_conversation`

**Delete a conversation**

Deletes a conversation: it no longer appears in `GET /v1/conversations`, and if a chat turn is running in it, the turn is asked to stop. The response reports `status: "archived"`. The call is safe to repeat: deleting a conversation that is already deleted answers `200` again. Only a conversation that is not yours, or that does not exist, answers `404 not_found`.

Access: API key required; free and paid accounts. Cost: no token cost.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `conversation_id` | path | string | yes | The conversation id, for example `3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01`. Allowed: at least 1 character. |

### Response fields (inside `data`)

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | The conversation id. |
| `status` | "archived" | Always `archived`: the conversation is gone from your lists. |

### Responses

200: Deleted. `meta.request_id` is present.

```json
{
  "data": {
    "id": "3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01",
    "status": "archived"
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
```

401: Missing, malformed, unknown or revoked API key.

```json
{
  "error": {
    "code": "invalid_key",
    "message": "Invalid or revoked API key",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_key"
  }
}
```

404: No conversation with this id exists for your account.

```json
{
  "error": {
    "code": "not_found",
    "message": "Conversation 3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01 not found",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#not_found"
  }
}
```

429: Over the account request budget for the hour. Wait for the `Retry-After` header, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rate_limit_exceeded"
  }
}
```

503: Temporary platform condition; retry shortly.

```json
{
  "error": {
    "code": "auth_unavailable",
    "message": "Authentication service temporarily unavailable",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#auth_unavailable"
  }
}
```

### Error codes

[`auth_unavailable`](https://www.scarleta.ai/docs/errors#auth_unavailable), [`delete_failed`](https://www.scarleta.ai/docs/errors#delete_failed), [`invalid_auth_format`](https://www.scarleta.ai/docs/errors#invalid_auth_format), [`invalid_key`](https://www.scarleta.ai/docs/errors#invalid_key), [`invalid_key_format`](https://www.scarleta.ai/docs/errors#invalid_key_format), [`missing_api_key`](https://www.scarleta.ai/docs/errors#missing_api_key), [`missing_auth`](https://www.scarleta.ai/docs/errors#missing_auth), [`missing_conversation_id`](https://www.scarleta.ai/docs/errors#missing_conversation_id), [`not_found`](https://www.scarleta.ai/docs/errors#not_found), [`rate_limit_exceeded`](https://www.scarleta.ai/docs/errors#rate_limit_exceeded), [`rate_limit_unavailable`](https://www.scarleta.ai/docs/errors#rate_limit_unavailable), [`unauthorized`](https://www.scarleta.ai/docs/errors#unauthorized)

### Example

```bash
CONVERSATION_ID="YOUR_CONVERSATION_ID"
curl -X DELETE "https://api.scarleta.ai/v1/conversations/$CONVERSATION_ID" \
  -H "Authorization: Bearer $SCARLETA_API_KEY"
```

## POST /v1/conversations/{conversation_id}/cancel

Operation id: `cancel_conversation_turn`

**Cancel the running turn**

Asks the running chat turn of a conversation to stop. The call is safe to repeat: when no turn is running it answers `200` with `signaled: false` and `delivery: "no_active_turn"`. `delivery` says whether the stop signal reached the turn: `sent`, `send_failed` (your intent is recorded but the signal did not arrive), `binding_missing`, or `stale` (the turn you saw ended and a new one started before the signal could be sent, so nothing was cancelled; call again to cancel the new turn).

Access: API key required; free and paid accounts. Cost: no token cost.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `conversation_id` | path | string | yes | The conversation id, for example `3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01`. Allowed: at least 1 character. |

### Response fields (inside `data`)

| Field | Type | Description |
| --- | --- | --- |
| `conversation_id` | string |   |
| `signaled` | boolean | True when a stop signal was recorded for a running turn. |
| `target_workflow_id` | string \| null | The turn the signal was aimed at, or null when none was running. |
| `delivery` | "sent" \| "send_failed" \| "binding_missing" \| "no_active_turn" \| "stale" | Outcome of delivering the signal. |

### Responses

200: The body is `{ data }` with no `meta` member; the request id is in the `X-Request-ID` header.

```json
{
  "data": {
    "conversation_id": "3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01",
    "signaled": true,
    "target_workflow_id": "turn_6e0ed523-72f5-464e-974c-9ae95fdc2a99",
    "delivery": "sent"
  }
}
```

401: Missing, malformed, unknown or revoked API key.

```json
{
  "error": {
    "code": "invalid_key",
    "message": "Invalid or revoked API key",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_key"
  }
}
```

404: No conversation with this id exists for your account.

```json
{
  "error": {
    "code": "not_found",
    "message": "Conversation 3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01 not found",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#not_found"
  }
}
```

429: Over the account request budget for the hour. Wait for the `Retry-After` header, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rate_limit_exceeded"
  }
}
```

502: The conversation could not be reached right now. Retry shortly.

```json
{
  "error": {
    "code": "conversation_do_error",
    "message": "Durable Object requestCancel failed: 500",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#conversation_do_error"
  }
}
```

503: Temporary platform condition; retry shortly.

```json
{
  "error": {
    "code": "auth_unavailable",
    "message": "Authentication service temporarily unavailable",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#auth_unavailable"
  }
}
```

### Error codes

[`auth_unavailable`](https://www.scarleta.ai/docs/errors#auth_unavailable), [`conversation_do_error`](https://www.scarleta.ai/docs/errors#conversation_do_error), [`conversation_do_unbound`](https://www.scarleta.ai/docs/errors#conversation_do_unbound), [`invalid_auth_format`](https://www.scarleta.ai/docs/errors#invalid_auth_format), [`invalid_key`](https://www.scarleta.ai/docs/errors#invalid_key), [`invalid_key_format`](https://www.scarleta.ai/docs/errors#invalid_key_format), [`missing_api_key`](https://www.scarleta.ai/docs/errors#missing_api_key), [`missing_auth`](https://www.scarleta.ai/docs/errors#missing_auth), [`missing_conversation_id`](https://www.scarleta.ai/docs/errors#missing_conversation_id), [`not_found`](https://www.scarleta.ai/docs/errors#not_found), [`rate_limit_exceeded`](https://www.scarleta.ai/docs/errors#rate_limit_exceeded), [`rate_limit_unavailable`](https://www.scarleta.ai/docs/errors#rate_limit_unavailable), [`unauthorized`](https://www.scarleta.ai/docs/errors#unauthorized)

### Example

```bash
CONVERSATION_ID="YOUR_CONVERSATION_ID"
curl -X POST "https://api.scarleta.ai/v1/conversations/$CONVERSATION_ID/cancel" \
  -H "Authorization: Bearer $SCARLETA_API_KEY"
```

## GET /v1/conversations/{conversation_id}/live-status

Operation id: `get_conversation_live_status`

**Get the progress of a conversation**

Reports how far a conversation has got: the newest exchange, whether an assistant reply arrived since `since_turn_index`, and, when you pass `job_id`, whether that job's result has been delivered into the conversation and the progress of its steps. Use it to show progress while a long job runs.

Access: API key required; free and paid accounts. Cost: no token cost.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `conversation_id` | path | string | yes | The conversation id, for example `3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01`. Allowed: at least 1 character. |
| `job_id` | query | string | no | A job started in this conversation, to get its step progress and delivery state. |
| `since_turn_index` | query | integer | no | Only count assistant replies after this exchange number. A value that is not an integer is ignored. Allowed: -9007199254740991 to 9007199254740991. |

### Response fields (inside `data`)

| Field | Type | Description |
| --- | --- | --- |
| `last_turn_index` | integer \| null | Number of the newest exchange; null when the conversation has no messages. Allowed: -9007199254740991 to 9007199254740991. |
| `last_message_at` | string \| null | ISO 8601 timestamp of the newest message, or null. |
| `has_new_assistant_result` | boolean | True when an assistant reply exists after `since_turn_index`. |
| `job_delivered` | boolean | True when the result of `job_id` has been delivered into the conversation. False when `job_id` is absent. |
| `job_delivered_turn_index` | integer \| null | The exchange the job result was delivered in, or null. Allowed: -9007199254740991 to 9007199254740991. |
| `progress` | array<object> | Steps of `job_id`; empty when `job_id` is absent. |
| `progress[].step_index` | integer | Allowed: -9007199254740991 to 9007199254740991. |
| `progress[].label` | string | Plain-language name of the step. |
| `progress[].status` | string | State of the step. |

### Responses

200: `meta.request_id` is present.

```json
{
  "data": {
    "last_turn_index": 1,
    "last_message_at": "2026-10-21T03:41:56.630Z",
    "has_new_assistant_result": true,
    "job_delivered": false,
    "job_delivered_turn_index": null,
    "progress": []
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
```

400: `job_id` has characters outside letters, digits and `_ . : -`, or is too long.

```json
{
  "error": {
    "code": "invalid_job_id",
    "message": "job_id must match ^[A-Za-z0-9_.:-]{1,128}$",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_job_id"
  }
}
```

401: Missing, malformed, unknown or revoked API key.

```json
{
  "error": {
    "code": "invalid_key",
    "message": "Invalid or revoked API key",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_key"
  }
}
```

404: No conversation with this id exists for your account.

```json
{
  "error": {
    "code": "not_found",
    "message": "Conversation 3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01 not found",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#not_found"
  }
}
```

429: Over the account request budget for the hour. Wait for the `Retry-After` header, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rate_limit_exceeded"
  }
}
```

503: Temporary platform condition; retry shortly.

```json
{
  "error": {
    "code": "auth_unavailable",
    "message": "Authentication service temporarily unavailable",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#auth_unavailable"
  }
}
```

### Error codes

[`auth_unavailable`](https://www.scarleta.ai/docs/errors#auth_unavailable), [`invalid_auth_format`](https://www.scarleta.ai/docs/errors#invalid_auth_format), [`invalid_job_id`](https://www.scarleta.ai/docs/errors#invalid_job_id), [`invalid_key`](https://www.scarleta.ai/docs/errors#invalid_key), [`invalid_key_format`](https://www.scarleta.ai/docs/errors#invalid_key_format), [`live_status_failed`](https://www.scarleta.ai/docs/errors#live_status_failed), [`missing_api_key`](https://www.scarleta.ai/docs/errors#missing_api_key), [`missing_auth`](https://www.scarleta.ai/docs/errors#missing_auth), [`missing_conversation_id`](https://www.scarleta.ai/docs/errors#missing_conversation_id), [`not_found`](https://www.scarleta.ai/docs/errors#not_found), [`rate_limit_exceeded`](https://www.scarleta.ai/docs/errors#rate_limit_exceeded), [`rate_limit_unavailable`](https://www.scarleta.ai/docs/errors#rate_limit_unavailable), [`unauthorized`](https://www.scarleta.ai/docs/errors#unauthorized)

### Example

```bash
CONVERSATION_ID="YOUR_CONVERSATION_ID"
curl "https://api.scarleta.ai/v1/conversations/$CONVERSATION_ID/live-status" \
  -H "Authorization: Bearer $SCARLETA_API_KEY"
```

## PUT /v1/conversations/{conversation_id}/mark-read

Operation id: `mark_conversation_read`

**Mark a conversation as read**

Clears the unread marker: `is_unread` in `GET /v1/conversations` becomes false for this conversation. The call has no request body and is safe to repeat.

Access: API key required; free and paid accounts. Cost: no token cost.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `conversation_id` | path | string | yes | The conversation id, for example `3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01`. Allowed: at least 1 character. |

### Response fields (inside `data`)

| Field | Type | Description |
| --- | --- | --- |
| `ok` | true | Always true. |

### Responses

200: Marked read. `meta.request_id` is present.

```json
{
  "data": {
    "ok": true
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
```

401: Missing, malformed, unknown or revoked API key.

```json
{
  "error": {
    "code": "invalid_key",
    "message": "Invalid or revoked API key",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_key"
  }
}
```

404: No conversation with this id exists for your account.

```json
{
  "error": {
    "code": "not_found",
    "message": "Conversation 3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01 not found",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#not_found"
  }
}
```

429: Over the account request budget for the hour. Wait for the `Retry-After` header, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rate_limit_exceeded"
  }
}
```

503: Temporary platform condition; retry shortly.

```json
{
  "error": {
    "code": "auth_unavailable",
    "message": "Authentication service temporarily unavailable",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#auth_unavailable"
  }
}
```

### Error codes

[`auth_unavailable`](https://www.scarleta.ai/docs/errors#auth_unavailable), [`invalid_auth_format`](https://www.scarleta.ai/docs/errors#invalid_auth_format), [`invalid_key`](https://www.scarleta.ai/docs/errors#invalid_key), [`invalid_key_format`](https://www.scarleta.ai/docs/errors#invalid_key_format), [`mark_read_failed`](https://www.scarleta.ai/docs/errors#mark_read_failed), [`missing_api_key`](https://www.scarleta.ai/docs/errors#missing_api_key), [`missing_auth`](https://www.scarleta.ai/docs/errors#missing_auth), [`missing_conversation_id`](https://www.scarleta.ai/docs/errors#missing_conversation_id), [`not_found`](https://www.scarleta.ai/docs/errors#not_found), [`rate_limit_exceeded`](https://www.scarleta.ai/docs/errors#rate_limit_exceeded), [`rate_limit_unavailable`](https://www.scarleta.ai/docs/errors#rate_limit_unavailable), [`unauthorized`](https://www.scarleta.ai/docs/errors#unauthorized)

### Example

```bash
CONVERSATION_ID="YOUR_CONVERSATION_ID"
curl -X PUT "https://api.scarleta.ai/v1/conversations/$CONVERSATION_ID/mark-read" \
  -H "Authorization: Bearer $SCARLETA_API_KEY"
```

## POST /v1/conversations/{conversation_id}/rewind

Operation id: `rewind_conversation`

**Rewind a conversation to an earlier message**

Use this to edit a message you already sent. It deletes the message you name, together with its reply and every later exchange, so you can send the edited message again with `POST /v1/chat`. **The deleted messages cannot be recovered.** `user_message_ordinal` is the position of the message among the messages you sent, counted from 0. The optional guards make the call delete nothing when your copy of the thread differs from the stored one: `message_id` (the id of a message read from `GET /v1/conversations/{conversation_id}`) picks the target directly, `user_messages_after` must equal the number of your messages that come after the target, and `expected_text` must equal the target's stored text. When no message matches, the call answers `409 rewind_target_mismatch` and deletes nothing. While a turn is running in the conversation, or an analysis recipe is running for it, the call answers `409 turn_in_progress` and deletes nothing.

Access: API key required; free and paid accounts. Cost: no token cost.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `conversation_id` | path | string | yes | The conversation id, for example `3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01`. Allowed: at least 1 character. |

### Request body

| Field | Type | Description |
| --- | --- | --- |
| `user_message_ordinal` | integer | Position of the message to edit among the messages you sent, counted from 0. Allowed: 0 to 9007199254740991. |
| `expected_text` | string | The text the target message had when you read it. Nothing is deleted if the stored text differs. |
| `message_id` | string | The id of the target message, at most 200 characters. When it names one of your messages it picks the target. |
| `user_messages_after` | integer | How many of your messages come after the target. Nothing is deleted if it does not match. Allowed: 0 to 9007199254740991. |

### Response fields (inside `data`)

| Field | Type | Description |
| --- | --- | --- |
| `deleted` | integer | Messages deleted: the target, its reply and everything after. Allowed: -9007199254740991 to 9007199254740991. |

### Responses

200: Rewound. `meta.request_id` is present.

```json
{
  "data": {
    "deleted": 2
  },
  "meta": {
    "request_id": "req_0b7a3c2e-6f1d-4d9a-8e55-3a4b5c6d7e8f"
  }
}
```

400: A missing or malformed body, or a field of the wrong type (the message names the field).

```json
{
  "error": {
    "code": "invalid_request",
    "message": "user_message_ordinal must be a non-negative integer",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_request"
  }
}
```

401: Missing, malformed, unknown or revoked API key.

```json
{
  "error": {
    "code": "invalid_key",
    "message": "Invalid or revoked API key",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_key"
  }
}
```

404: No conversation with this id exists for your account.

```json
{
  "error": {
    "code": "not_found",
    "message": "Conversation 3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01 not found",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#not_found"
  }
}
```

409: No message matches `user_message_ordinal` and the guards (`turn_in_progress` when a turn is running). Nothing was deleted.

```json
{
  "error": {
    "code": "rewind_target_mismatch",
    "message": "The edited message does not match this conversation. Reload and try again.",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rewind_target_mismatch"
  }
}
```

429: Over the account request budget for the hour. Wait for the `Retry-After` header, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rate_limit_exceeded"
  }
}
```

502: The conversation could not be reached right now. Retry shortly.

```json
{
  "error": {
    "code": "conversation_do_error",
    "message": "Durable Object tryAcquireTurn failed: 500",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#conversation_do_error"
  }
}
```

503: Temporary platform condition; retry shortly.

```json
{
  "error": {
    "code": "auth_unavailable",
    "message": "Authentication service temporarily unavailable",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#auth_unavailable"
  }
}
```

### Error codes

[`auth_unavailable`](https://www.scarleta.ai/docs/errors#auth_unavailable), [`conversation_do_error`](https://www.scarleta.ai/docs/errors#conversation_do_error), [`conversation_do_unbound`](https://www.scarleta.ai/docs/errors#conversation_do_unbound), [`invalid_auth_format`](https://www.scarleta.ai/docs/errors#invalid_auth_format), [`invalid_key`](https://www.scarleta.ai/docs/errors#invalid_key), [`invalid_key_format`](https://www.scarleta.ai/docs/errors#invalid_key_format), [`invalid_request`](https://www.scarleta.ai/docs/errors#invalid_request), [`missing_api_key`](https://www.scarleta.ai/docs/errors#missing_api_key), [`missing_auth`](https://www.scarleta.ai/docs/errors#missing_auth), [`missing_conversation_id`](https://www.scarleta.ai/docs/errors#missing_conversation_id), [`not_found`](https://www.scarleta.ai/docs/errors#not_found), [`rate_limit_exceeded`](https://www.scarleta.ai/docs/errors#rate_limit_exceeded), [`rate_limit_unavailable`](https://www.scarleta.ai/docs/errors#rate_limit_unavailable), [`rewind_failed`](https://www.scarleta.ai/docs/errors#rewind_failed), [`rewind_target_mismatch`](https://www.scarleta.ai/docs/errors#rewind_target_mismatch), [`turn_in_progress`](https://www.scarleta.ai/docs/errors#turn_in_progress), [`unauthorized`](https://www.scarleta.ai/docs/errors#unauthorized)

### Example

```bash
CONVERSATION_ID="YOUR_CONVERSATION_ID"
curl -X POST "https://api.scarleta.ai/v1/conversations/$CONVERSATION_ID/rewind" \
  -H "Authorization: Bearer $SCARLETA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_message_ordinal": 0
  }'
```

## GET /v1/conversations/{conversation_id}/status

Operation id: `get_conversation_status`

**Get the active turn of a conversation**

Tells you whether a chat turn is running in the conversation. After `POST /v1/chat`, poll this route every few seconds: `active_turn_workflow_id` is the id of the running turn and becomes null when the turn is over. Then read the reply with `GET /v1/conversations/{conversation_id}`.

Access: API key required; free and paid accounts. Cost: no token cost.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `conversation_id` | path | string | yes | The conversation id, for example `3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01`. Allowed: at least 1 character. |

### Response fields (inside `data`)

| Field | Type | Description |
| --- | --- | --- |
| `conversation_id` | string |   |
| `active_turn_workflow_id` | string \| null | The running turn, or null when none is running. |
| `started_at` | number \| null | When the running turn started, in milliseconds since the Unix epoch; null when none is running. |
| `tenant_id` | string \| null | Your account identifier; null for free accounts. |

### Responses

200: The body is `{ data }` with no `meta` member; the request id is in the `X-Request-ID` header.

```json
{
  "data": {
    "conversation_id": "3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01",
    "active_turn_workflow_id": "turn_6e0ed523-72f5-464e-974c-9ae95fdc2a99",
    "started_at": 1792554134633,
    "tenant_id": "8c1d0b52-3f6a-4e0d-9d7b-2a41c5e90f13"
  }
}
```

401: Missing, malformed, unknown or revoked API key.

```json
{
  "error": {
    "code": "invalid_key",
    "message": "Invalid or revoked API key",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#invalid_key"
  }
}
```

404: No conversation with this id exists for your account.

```json
{
  "error": {
    "code": "not_found",
    "message": "Conversation 3f9c1d2e-7b44-4c1a-8e0f-5a6b7c8d9e01 not found",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#not_found"
  }
}
```

429: Over the account request budget for the hour. Wait for the `Retry-After` header, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#rate_limit_exceeded"
  }
}
```

502: The conversation could not be read right now. Retry shortly.

```json
{
  "error": {
    "code": "conversation_do_error",
    "message": "Durable Object getActiveTurn failed: 500",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#conversation_do_error"
  }
}
```

503: Temporary platform condition; retry shortly.

```json
{
  "error": {
    "code": "auth_unavailable",
    "message": "Authentication service temporarily unavailable",
    "request_id": "req_5b1c0f3e-2d41-4a8e-9a57-0c1b2d3e4f50",
    "docs_url": "https://www.scarleta.ai/docs/errors#auth_unavailable"
  }
}
```

### Error codes

[`auth_unavailable`](https://www.scarleta.ai/docs/errors#auth_unavailable), [`conversation_do_error`](https://www.scarleta.ai/docs/errors#conversation_do_error), [`conversation_do_unbound`](https://www.scarleta.ai/docs/errors#conversation_do_unbound), [`invalid_auth_format`](https://www.scarleta.ai/docs/errors#invalid_auth_format), [`invalid_key`](https://www.scarleta.ai/docs/errors#invalid_key), [`invalid_key_format`](https://www.scarleta.ai/docs/errors#invalid_key_format), [`missing_api_key`](https://www.scarleta.ai/docs/errors#missing_api_key), [`missing_auth`](https://www.scarleta.ai/docs/errors#missing_auth), [`missing_conversation_id`](https://www.scarleta.ai/docs/errors#missing_conversation_id), [`not_found`](https://www.scarleta.ai/docs/errors#not_found), [`rate_limit_exceeded`](https://www.scarleta.ai/docs/errors#rate_limit_exceeded), [`rate_limit_unavailable`](https://www.scarleta.ai/docs/errors#rate_limit_unavailable), [`unauthorized`](https://www.scarleta.ai/docs/errors#unauthorized)

### Example

```bash
CONVERSATION_ID="YOUR_CONVERSATION_ID"
curl "https://api.scarleta.ai/v1/conversations/$CONVERSATION_ID/status" \
  -H "Authorization: Bearer $SCARLETA_API_KEY"
```
