Skip to main content

Attachments

Downloading an attachment

GET /v1/attachments/{id}/download

This endpoint responds with a 302 redirect to a short-lived presigned URL where the file is hosted. Configure your HTTP client to follow redirects (most do by default):

curl -L "https://api.abcsalesbot.com/v1/attachments/{id}/download" \
-H "Authorization: Bearer $ABC_API_KEY" \
-o attachment.bin

The presigned URL expires, so request the download fresh each time rather than caching the redirected URL.

Sending an attachment

When replying to a conversation, include the s3_attachment field to attach a file:

{
"content": "Here is the document you asked for.",
"messaging_channel_connection_id": 123,
"s3_attachment": "<storage reference>"
}

See the API Reference for the exact s3_attachment format accepted by the reply endpoint.

Uploading a file for a "file" custom field

Custom fields of type file (a candidate's resume or video, a signed document) hold a reference to a file you upload first. Uploading is a three-step presign, upload, confirm flow, because the file goes straight to storage rather than through the API.

Use Discovery to find which of your custom fields are of type file.

1. Request a presigned URL

curl "https://api.abcsalesbot.com/v1/attachments/custom-field-file-url" \
-H "Authorization: Bearer $ABC_API_KEY"
{
"data": {
"presigned_url": "https://s3.amazonaws.com/...",
"max_size_bytes": 524288000,
"allowed_content_types": [
"video/mp4",
"video/quicktime",
"video/webm",
"application/pdf",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"image/jpeg",
"image/png",
"image/webp"
]
}
}

max_size_bytes and allowed_content_types are returned so you can check a file before spending upload bandwidth on it. They are not enforced by the presigned URL itself, they are enforced for real in step 3.

2. Upload the file

PUT the raw bytes to presigned_url:

curl -X PUT "$PRESIGNED_URL" \
--upload-file ./resume.pdf

The presigned URL is short-lived. Request a fresh one if the upload does not start promptly.

3. Confirm the upload

curl -X POST "https://api.abcsalesbot.com/v1/attachments/custom-field-file-url" \
-H "Authorization: Bearer $ABC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"file_url": "'"$PRESIGNED_URL"'",
"file_name": "resume.pdf"
}'

Both file_url and file_name are required, and a body missing either returns 400.

Confirming is what validates the file and turns it into a durable attachment. The server inspects the actual uploaded bytes (it does not trust a Content-Type you send) and checks them against the size and type policy from step 1.

{
"data": {
"id": 4567,
"file_name": "resume.pdf",
"original_file_name": "resume.pdf",
"content_type": "application/pdf",
"extension": "pdf",
"s3_path": "attachments-uploads/...",
"url": "https://...",
"company_id": 12,
"created_at": "2026-08-07T10:00:00Z"
}
}
A rejected file is deleted

If the uploaded file is empty, exceeds max_size_bytes, or is not one of allowed_content_types, confirm returns 400 and deletes the object from storage. It never becomes an attachment, and you must upload again from step 1.

4. Reference it on the lead

Set the s3_path from the confirm response as the value of your file custom field when sending the lead in:

{
"name": "Jane Doe",
"phone": "+60123456789",
"custom_fields": {
"candidate_resume": "attachments-uploads/..."
}
}

Rate limit

The presign endpoint (GET /v1/attachments/custom-field-file-url) is limited to 60 requests per 10 minutes per company, since each call mints write capability against storage. Confirm is not separately limited. See Rate Limits.