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

# Upload an attachment

> Returns a short-lived presigned S3 POST (expires in 5 minutes / 300 seconds). Submit a multipart/form-data request to `uploadUrl` with every `fields` entry followed by the `file` field last; the `Content-Type` you send must match the one in `fields`, and S3 enforces the size limit server-side. Then reference the returned `fileUrl`/metadata in the `attachments` array of a reply or conversation message. Supported types and max sizes: images png/jpg/jpeg/webp (10 MB), documents pdf/docx/xlsx/pptx (25 MB), text txt/csv (25 MB), video mp4/3gpp/webm/mpeg (50 MB), audio mp3/wav/ogg/mpeg (50 MB). Other types — including svg, html and javascript — are rejected.

## Overview

Attachments are **not** uploaded through this API directly. Instead you exchange
the file's metadata for a short-lived, pre-signed Amazon S3 upload, push the file
straight to S3, and then reference the returned URL when you send a message. This
keeps large files off our servers and lets uploads scale.

The flow has **three steps**:

<Steps>
  <Step title="Request a pre-signed upload">
    Call this endpoint with the file's `mimeType`, `size` and a `fileName`. You
    get back an `uploadUrl`, a set of `fields`, and the final `fileUrl`. The
    conversation's chatbot is resolved server-side from the `conversationId` —
    you don't pass a `chatbotId`.
  </Step>

  <Step title="Upload the file to S3">
    Send a `multipart/form-data` POST to `uploadUrl` containing every entry in
    `fields` **first**, then the `file` **last**.
  </Step>

  <Step title="Send the message">
    Call [Send message](/api-reference-v2/conversations/send-message) or
    [Reply to a conversation](/api-reference-v2/conversations/reply) with an
    `attachments` array that references the `fileUrl` and metadata.
  </Step>
</Steps>

<Note>
  The conversation must already exist. The `conversationId` is validated against
  your workspace, so you can only upload into your own conversations.
</Note>

## Step 1 — Request a pre-signed upload

```bash theme={null}
curl -X POST \
  'https://api.popupsmart.com/api/v2/conversations/{conversationId}/attachments/presign' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "fileName": "report.pdf",
    "mimeType": "application/pdf",
    "size": 248213
  }'
```

Response:

```json theme={null}
{
  "status": "success",
  "data": {
    "uploadUrl": "https://cdn.example.com",
    "fields": {
      "key": "clx123.../chat/chat_123/messages/9f8e7d.pdf",
      "Content-Type": "application/pdf",
      "Content-Disposition": "attachment",
      "acl": "public-read",
      "policy": "eyJ...",
      "x-amz-signature": "a1b2c3..."
    },
    "fileUrl": "https://cdn.example.com/clx123.../chat/chat_123/messages/9f8e7d.pdf",
    "name": "report.pdf",
    "mimeType": "application/pdf",
    "size": 248213,
    "category": "doc"
  }
}
```

<Warning>
  The pre-signed upload expires in **5 minutes (300 seconds)**. Upload the file
  promptly, or request a new one.
</Warning>

## Step 2 — Upload the file to S3

POST the file to `uploadUrl` as `multipart/form-data`. **Append every key from
`fields` in order, then append the `file` field last** — S3 ignores any field
that comes after `file`.

```bash theme={null}
curl -X POST 'https://cdn.example.com' \
  -F 'key=clx123.../chat/chat_123/messages/9f8e7d.pdf' \
  -F 'Content-Type=application/pdf' \
  -F 'Content-Disposition=attachment' \
  -F 'acl=public-read' \
  -F 'policy=eyJ...' \
  -F 'x-amz-signature=a1b2c3...' \
  -F 'file=@report.pdf'
```

A successful upload returns `204 No Content`. The `Content-Type` you send **must
match** the `Content-Type` in `fields`, and the file size must be within the
[per-category limit](#allowed-types-and-size-limits) — S3 rejects the upload
otherwise, even if step 1 succeeded.

<Tip>
  In a browser, build a `FormData` object: append each `fields` entry with
  `formData.append(key, value)`, then `formData.append('file', file)` last, and
  `fetch(uploadUrl, { method: 'POST', body: formData })`.
</Tip>

## Step 3 — Send the message with the attachment

Reference the `fileUrl` and metadata in the `attachments` array. The message type
is resolved automatically (a single image → `IMAGE`, anything else → `FILE`).

```bash theme={null}
curl -X POST \
  'https://api.popupsmart.com/api/v2/conversations/{conversationId}/reply' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "userId": "user_1234567890",
    "message": "Here is the report you asked for.",
    "attachments": [
      {
        "url": "https://cdn.example.com/clx123.../chat/chat_123/messages/9f8e7d.pdf",
        "name": "report.pdf",
        "mimeType": "application/pdf",
        "size": 248213
      }
    ]
  }'
```

Every attachment is re-validated server-side before it is stored; invalid entries
are dropped. The stored attachment is returned with an `id` and a resolved
`category` (`image` | `video` | `audio` | `doc` | `text`).

## Allowed types and size limits

Only the MIME types below are accepted. Anything else — including `image/svg+xml`,
`text/html`, and JavaScript types — is rejected for security.

| Category | MIME types                                                         | Max size |
| -------- | ------------------------------------------------------------------ | -------- |
| `image`  | `image/png`, `image/jpg`, `image/jpeg`, `image/webp`               | 10 MB    |
| `doc`    | `application/pdf`, `.docx`, `.xlsx`, `.pptx` (Office Open XML)     | 25 MB    |
| `text`   | `text/plain`, `text/csv`                                           | 25 MB    |
| `video`  | `video/mp4`, `video/3gpp`, `video/webm`, `video/mpeg`              | 50 MB    |
| `audio`  | `audio/mp3`, `audio/mpeg`, `audio/wav`, `audio/x-wav`, `audio/ogg` | 50 MB    |

## Errors

| Status | When                                                                                                      |
| ------ | --------------------------------------------------------------------------------------------------------- |
| `400`  | Missing `fileName`/`mimeType`/`size`, an unsupported MIME type, or a file larger than the category limit. |
| `401`  | Missing or invalid API key.                                                                               |
| `404`  | The conversation does not exist or does not belong to your workspace.                                     |


## OpenAPI

````yaml post /api/v2/conversations/{conversationId}/attachments/presign
openapi: 3.0.1
info:
  title: LiveChatAI API v2
  description: >-
    LiveChatAI API v2 - Build powerful AI-powered customer support experiences.
    This API follows REST principles with predictable, resource-oriented URLs,
    JSON responses, and standard HTTP methods.
  version: 2.0.0
servers:
  - url: https://app.livechatai.com
    description: Production server
security:
  - bearerAuth: []
paths:
  /api/v2/conversations/{conversationId}/attachments/presign:
    post:
      tags:
        - Conversations
      summary: Get a presigned URL to upload a message attachment
      description: >-
        Returns a short-lived presigned S3 POST (expires in 5 minutes / 300
        seconds). Submit a multipart/form-data request to `uploadUrl` with every
        `fields` entry followed by the `file` field last; the `Content-Type` you
        send must match the one in `fields`, and S3 enforces the size limit
        server-side. Then reference the returned `fileUrl`/metadata in the
        `attachments` array of a reply or conversation message. Supported types
        and max sizes: images png/jpg/jpeg/webp (10 MB), documents
        pdf/docx/xlsx/pptx (25 MB), text txt/csv (25 MB), video
        mp4/3gpp/webm/mpeg (50 MB), audio mp3/wav/ogg/mpeg (50 MB). Other types
        — including svg, html and javascript — are rejected.
      parameters:
        - in: path
          name: conversationId
          schema:
            type: string
            example: chat_1234567890
          required: true
          description: Unique identifier of the conversation the attachment belongs to
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - fileName
                - mimeType
                - size
              properties:
                fileName:
                  type: string
                  example: report.pdf
                mimeType:
                  type: string
                  example: application/pdf
                size:
                  type: integer
                  description: File size in bytes
      responses:
        '200':
          description: Presigned upload URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    type: object
                    properties:
                      uploadUrl:
                        type: string
                        description: >-
                          Presigned S3 POST endpoint. Expires shortly. Submit a
                          multipart/form-data request here with every `fields`
                          entry plus the file last.
                      fields:
                        type: object
                        additionalProperties:
                          type: string
                        description: >-
                          Form fields that must be included (before the file) in
                          the multipart upload to S3.
                      fileUrl:
                        type: string
                        description: Public URL to reference once the upload completes.
                      name:
                        type: string
                      mimeType:
                        type: string
                      size:
                        type: integer
                      category:
                        type: string
                        enum:
                          - image
                          - video
                          - audio
                          - doc
                          - text
        '400':
          description: >-
            Missing fileName/mimeType/size, an unsupported MIME type, or a file
            larger than the category limit
        '401':
          description: Unauthorized access
        '404':
          description: Conversation not found or not in your workspace
      security:
        - bearerAuth: []
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````