media parts. You can upload a file once and reuse it by its attachment_id, or point Blujay at a public HTTPS URL for a one-off send. Media you receive arrives as media parts too, and you download it through the API.
Choose how to send a file
Each
media part sets exactly one of attachment_id or url. A message can have up to 40 media and vcard parts.
Upload a file
Declare the file
Tell Blujay the file’s name, type and exact size in bytes:
201 Created
content_type is a lowercase MIME type without parameters, such as image/jpeg. filename is a plain file name with no folder path.upload_url is a signed URL on Blujay’s storage, not on api.blujay.io. The host is returned in the response; allow it in your egress rules rather than hardcoding one.PUT the raw bytes
Send the file’s raw bytes to The URL expires after 15 minutes. If it expires before you upload, declare the file again.
upload_url with exactly the upload_headers from the response. Don’t use multipart/form-data, and don’t add your API key: the URL already authorizes this one upload.- If the upload hasn’t finished, you get
409ATTACHMENT_NOT_READY. Retry once thePUTcompletes. - If the bytes don’t match the declared type, you get
422ATTACHMENT_UPLOAD_INVALID. Declare the file again and upload the correct bytes. - If the bytes don’t match the declared
size_bytes, the storage service refuses thePUTitself with a403and an XML body (the signed URL is bound to the exact size). Nothing is stored and the attachment stays409ATTACHMENT_NOT_READY. Declare the file again with the right size and upload again.
attachment_id in as many messages as you like.
At declaration, a file over 100 MiB returns 413 ATTACHMENT_TOO_LARGE. Executables, scripts, HTML, SVG and XML files return 415 UNSUPPORTED_ATTACHMENT_TYPE. If your workspace has too many uploads waiting for their bytes, you get 429 ATTACHMENT_CAPACITY_EXCEEDED; finish or abandon some uploads and retry.
Accepted file types
Blujay identifies a file by its bytes, not only bycontent_type, and accepts these:
A file whose bytes don’t match the specific type you declared is rejected with
422 ATTACHMENT_UPLOAD_INVALID when the upload is checked. A file Blujay can’t identify is accepted as application/octet-stream; declare it as that. What a service can carry is a separate question; see What each service can carry.
Send from a URL
For a one-off image, video or contact card, put a public HTTPS URL in the part:202 Accepted
attachment_id.
- The URL must be public HTTPS, up to 2,048 characters. URLs with credentials or that point at private addresses are rejected with
422ATTACHMENT_FETCH_FAILEDbefore anything is sent. - The file can be up to 10 MiB and must be an image, a video or a vCard. Serve a vCard as
text/vcardortext/x-vcard, or as a generic binary type with a.vcffile name. - Files disguised as another type are rejected.
failure_code: ATTACHMENT_FETCH_FAILED, ATTACHMENT_TOO_LARGE or UNSUPPORTED_ATTACHMENT_TYPE. See Track a message.
What each service can carry
The service that carries a message decides which files it can send:
If any file in a message can’t go out on the service that carries it, the whole message fails. Blujay never silently drops, converts or shrinks a file. If you might send to SMS recipients, keep files small and stick to images and videos, or check the number first.
Read and download media
Message parts show media with its details once the file is ready:download_url is a path on the Blujay API. Download the file from https://api.blujay.io plus that path, with your usual Authorization header:
Content-Type and a Content-Disposition file name.
To get an attachment’s details without downloading it, call GET /v1/attachments/{attachment_id}:
Media in received messages
Media in an inbound message can show up before the file is ready. At first, the part may have onlytype and attachment_id. The message.received webhook is a snapshot and never changes, and there is no “attachment ready” event. To get the file:
- Take
attachment_idfrom themessage.receivedevent or the message read. - Call
GET /v1/attachments/{attachment_id}. While the file is still arriving, it returns409ATTACHMENT_NOT_READY. - Wait a few seconds and call again, with a growing delay. Give up after a few minutes and log the
attachment_id. - Once the response has
status: ready, readfilename,content_type,size_bytesanddownload_url, then download the file.
Delete an attachment
DELETE /v1/attachments/{attachment_id} stops an uploaded attachment from being sent or downloaded again, effective immediately. Messages that already include it keep the reference in their history. Deleting an attachment you already deleted returns 204 again. An attachment_id that doesn’t exist in your workspace returns 404 NOT_FOUND.
Limits
Attachment endpoints have their own request limit, separate from other reads and writes. See Rate limits. If attachment storage is briefly unavailable, you get503 ATTACHMENT_STORAGE_UNAVAILABLE; retry shortly.
Limitations
- No media until the contact replies (
400PRE_REPLY_CONTENT_BLOCKED). - Uploads up to 100 MiB; URL sends up to 10 MiB and only images, videos and vCards (
413ATTACHMENT_TOO_LARGE,415UNSUPPORTED_ATTACHMENT_TYPE). - Executables, scripts, HTML, SVG and XML are never accepted (
415UNSUPPORTED_ATTACHMENT_TYPE). - RCS and SMS carry only images, videos and vCards; SMS/MMS up to 5 MiB per message. A message with a file the service can’t carry fails as a whole.
- Voice memos aren’t supported. Audio goes out as a file.
- There is no attachment list endpoint and no “attachment ready” event. Poll
GET /v1/attachments/{attachment_id}. - Deleting an attachment doesn’t remove it from messages that already sent it.
Next steps
Contact cards
Send a saved vCard as a native contact card.
Send messages
Message parts and follow-ups.
Track a message
Statuses and media failure codes.
Receive messages
Handle inbound media as it arrives.