Skip to main content
POST
/
api
/
v2
/
conversations
/
{conversationId}
/
attachments
/
presign
Get a presigned URL to upload a message attachment
curl --request POST \
  --url https://app.livechatai.com/api/v2/conversations/{conversationId}/attachments/presign \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "fileName": "report.pdf",
  "mimeType": "application/pdf",
  "size": 123
}
'
{
  "status": "success",
  "data": {
    "uploadUrl": "<string>",
    "fields": {},
    "fileUrl": "<string>",
    "name": "<string>",
    "mimeType": "<string>",
    "size": 123
  }
}

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:
1

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.
2

Upload the file to S3

Send a multipart/form-data POST to uploadUrl containing every entry in fields first, then the file last.
3

Send the message

Call Send message or Reply to a conversation with an attachments array that references the fileUrl and metadata.
The conversation must already exist. The conversationId is validated against your workspace, so you can only upload into your own conversations.

Step 1 — Request a pre-signed upload

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:
{
  "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"
  }
}
The pre-signed upload expires in 5 minutes (300 seconds). Upload the file promptly, or request a new one.

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.
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 — S3 rejects the upload otherwise, even if step 1 succeeded.
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 }).

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).
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.
CategoryMIME typesMax size
imageimage/png, image/jpg, image/jpeg, image/webp10 MB
docapplication/pdf, .docx, .xlsx, .pptx (Office Open XML)25 MB
texttext/plain, text/csv25 MB
videovideo/mp4, video/3gpp, video/webm, video/mpeg50 MB
audioaudio/mp3, audio/mpeg, audio/wav, audio/x-wav, audio/ogg50 MB

Errors

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

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Path Parameters

conversationId
string
required

Unique identifier of the conversation the attachment belongs to

Example:

"chat_1234567890"

Body

application/json
fileName
string
required
Example:

"report.pdf"

mimeType
string
required
Example:

"application/pdf"

size
integer
required

File size in bytes

Response

Presigned upload URL

status
string
Example:

"success"

data
object