:::endpoint POST /v1/me/ai/conversations

Makes a conversation, stores the question in it and queues the answer. The
response comes back at once, before the assistant has written a word: the
question as it was stored, and the answer as an empty message with `status`
`streaming`.

Draw both straight away, subscribe to the conversation's `channel`, and let
the [websocket events](/docs/api/apps/ai-overview#listening-for-the-answer)
fill the answer in. Match them by the answer's `id`, which is the `message_id`
of every `turn_state`.

## Request

```bash
curl -X POST https://api.proppertrading.com/v1/me/ai/conversations \
  -H "Authorization: Bearer ptat_9f2c1a44_3b8e7d2f5c9a1b4e6d8f0a2c4e6b8d1f" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6c1f0d52-3a8e-4b7d-9e21-5f4a8c3b2d10" \
  -d '{
    "message": "What is the maximum daily loss on my account?",
    "attachments": [
      { "name": "chart.png", "data": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAI..." }
    ]
  }'
```

| Field | Required | Description |
| ----- | -------- | ----------- |
| `message` | yes | The question, at most 4000 characters |
| `attachments` | no | Up to five files |
| `attachments[].name` | yes | The file's name as the person sees it, at most 255 characters. Never used as a path on our side |
| `attachments[].data` | yes | The file, base64. At most 10 MB once decoded. JPEG, PNG, GIF, WebP, PDF, plain text or CSV, worked out from the bytes |

## Response

`201`:

```json
{
    "data": {
        "object": "ai_turn",
        "conversation": {
            "id": "01a16152-7e42-70cd-91a4-b6b80ee47daa",
            "object": "ai_conversation",
            "title": null,
            "pinned": false,
            "surface": "trader_chat",
            "channel": "private-ai-chat.01a16152-7e42-70cd-91a4-b6b80ee47daa",
            "last_message_at": "2026-10-22T01:55:58+00:00",
            "created_at": "2026-10-22T01:55:58+00:00"
        },
        "question": {
            "id": "aimsg_dkr8n8ctvh9qtmce",
            "object": "ai_message",
            "role": "user",
            "content": "What is the maximum daily loss on my account?",
            "status": "complete",
            "attachments": [
                {
                    "index": 0,
                    "name": "chart.png",
                    "content_type": "image/png",
                    "size_bytes": 90,
                    "url": "https://api.proppertrading.com/v1/me/ai/messages/aimsg_dkr8n8ctvh9qtmce/attachments/0"
                }
            ],
            "actions": [],
            "created_at": "2026-10-22T01:55:58+00:00"
        },
        "answer": {
            "id": "aimsg_tjqupx579w3cpx9m",
            "object": "ai_message",
            "role": "assistant",
            "content": "",
            "status": "streaming",
            "attachments": [],
            "actions": [],
            "created_at": "2026-10-22T01:55:58+00:00"
        }
    },
    "meta": {
        "request_id": "req_01m5gn4zh3jdhfxy2bxdyg8q29"
    }
}
```

A message is the same shape everywhere:

| Field | Description |
| ----- | ----------- |
| `id` | A public id such as `aimsg_dkr8n8ctvh9qtmce` |
| `role` | `user` for the question, `assistant` for the answer |
| `content` | The text. Markdown in an answer |
| `status` | `complete`, `streaming`, `cancelled` or `failed` |
| `attachments` | The files on a question. `content_type` is the image type for an image and `application/octet-stream` for anything else. `url` [downloads it](/docs/api/apps/ai-attachment) with the same token |
| `actions` | The [action cards](/docs/api/apps/ai-overview#action-cards) an answer proposed, oldest first, as they stand now. Always empty on a question |

What the answer uses counts against the firm's AI allowance and, for a trader,
against a limit per day when the firm set one. A stopped answer counts for what
it wrote.

## Errors

| Status | Code | Meaning |
| ------ | ---- | ------- |
| 400 | `invalid_attachment` | A file's `data` is not base64, or it is empty. `param` says which, such as `attachments.0.data` |
| 400 | `attachment_too_large` | A file over 10 MB once decoded |
| 400 | `missing_idempotency_key` | Every POST needs one |
| 400 | `workspace_required` | Our own staff, without a firm in `Propper-Workspace` |
| 403 | `ai_unavailable` | The firm has the assistant switched off for this chat |
| 403 | `ai_kyc_required` | A trader whose account is locked until the identity check is done |
| 403 | `forbidden` | Staff whose role does not include `use_ai_chat` |
| 403 | `insufficient_scope` | This device does not hold `ai:write` |
| 422 | `validation_failed` | No `message`, one over 4000 characters, more than five files, or a file of a type that is not allowed |
| 429 | `ai_limit_reached` | An allowance is used up. `state` says which and `resets_at` when it comes back |
| 429 | `rate_limit_exceeded` | Twenty questions and deletions a minute, shared with the website |

A refusal from the allowance looks like this:

```json
{
    "error": {
        "type": "rate_limit_error",
        "code": "ai_limit_reached",
        "message": "You have asked as many questions today as this firm allows. Ask again after resets_at.",
        "doc_url": "https://proppertrading.com/docs/api/errors#ai_limit_reached",
        "state": "daily_limit",
        "resets_at": "2026-10-23T00:00:00+02:00",
        "request_id": "req_01kz4bkr1aj1gv63d8fv3133ey"
    }
}
```

`state` is `session` or `week` for the firm's AI allowance, `none` for a
package without AI, `platform` when our own capacity is used up, and
`daily_limit` for the limit per trader the firm set. `resets_at` is left out
when waiting will not help.