Send attachments
Include media URLs when sending messages
Supported attachments
Pass an array of publicly accessible URLs in the attachments field. Common formats: images, PDFs, and other files.
cURL
curl -X POST 'https://api.blooio.com/v2/api/chats/%2B15551234567/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"attachments": [
"https://cdn.example.com/image.png"
],
"metadata": { "order_id": "123" }
}'Try itNode.js
const chatId = encodeURIComponent('+15551234567')
await fetch(`https://api.blooio.com/v2/api/chats/${chatId}/messages`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BLOOIO_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
attachments: ['https://cdn.example.com/image.png']
})
})Python
from urllib.parse import quote
chat_id = quote('+15551234567', safe='')
requests.post(f'https://api.blooio.com/v2/api/chats/{chat_id}/messages',
headers={
'Authorization': f"Bearer {os.environ['BLOOIO_API_KEY']}",
'Content-Type': 'application/json'
},
json={
'attachments': ['https://cdn.example.com/image.png']
}
)Named attachments
You can also provide attachment objects with custom filenames:
{
"attachments": [
{ "url": "https://cdn.example.com/doc.pdf", "name": "contract.pdf" }
]
}Photo & video carousels (iMessage)
On the v4 send API, several images and/or videos are grouped into a single message that Messages renders as a swipeable photo/video carousel/collage — one bubble instead of several separate bubbles.
This is the default. On a Blooio iMessage send whose attachments are two or
more image/video URLs, the carousel layout is applied automatically — you don't
need to set anything. To send those attachments as separate bubbles instead, set
carousel: false.
Note
Carousels are only available on Blooio iMessage channels. The default-on behavior only applies there; images sent over Twilio/MMS or other channels are never grouped. Explicitly setting
carousel: trueon another channel type returns422 carousel_unsupported_for_channel_type.
Send from an attachments array
The common form — an optional text becomes the carousel's caption. Because the
two attachments are images, this is grouped into a carousel automatically:
curl -X POST 'https://api.blooio.com/v4/channels/CHANNEL_ID/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"to": "+15551234567",
"text": "Here are the photos!",
"attachments": [
"https://cdn.example.com/photo1.jpg",
"https://cdn.example.com/clip.mov",
"https://cdn.example.com/photo2.png"
]
}'Try itTo send these as three separate image bubbles instead, add "carousel": false.
Send from parts
You can also use an explicit parts array. When every part is a media
attachment (no text parts), it's grouped into a carousel automatically:
{
"to": "+15551234567",
"parts": [
{ "url": "https://cdn.example.com/photo1.jpg" },
{ "url": "https://cdn.example.com/photo2.jpg" }
]
}What will (and won't) send
Every attachment must be an image or video. The type is inferred from the URL's file extension:
- Images:
jpg,jpeg,png,gif,heic,heif,webp,bmp,tif,tiff,avif - Videos:
mov,mp4,m4v,qt,3gp,avi,mkv,webm
The default-on grouping only fires when a send qualifies — two or more
attachments that are all images/videos. Anything that doesn't qualify is simply
left ungrouped (no error); it's only a hard error when you set carousel: true
explicitly and the send breaks a rule.
| Send | Result |
|---|---|
| 2+ image URLs (nothing set) | ✅ Grouped into one photo carousel automatically |
2+ image URLs, carousel: false |
✅ Sent as separate image bubbles |
| Mixed images and videos | ✅ Grouped into one photo/video collage |
| A URL with no extension (signed / CDN URL) | ✅ Allowed — the real type is resolved on the device at send |
A URL with a query string (?token=…) |
✅ Only the path is checked, so this is fine |
A single image + a .pdf (nothing set) |
✅ Sent ungrouped — doesn't qualify, so it's left as-is |
A URL with a known non-visual extension (.pdf, .zip, …) + carousel: true |
❌ 422 invalid_carousel |
Only one attachment + carousel: true |
❌ 422 invalid_carousel (a carousel needs at least two) |
A text part mixed into parts + carousel: true |
❌ 422 invalid_carousel (use top-level text for a caption) |
carousel: true on a non-media send (rich link, poll, …) |
❌ 422 invalid_content |
A non-boolean carousel value |
❌ 422 invalid_content |
Warning
Grouping images/videos into one collage is now the default on Blooio iMessage. Set
carousel: falseto opt out and send separate bubbles. Settingcarousel: trueexplicitly additionally enforces the visual-only rule, so a non-visual attachment or a text part then errors instead of being sent ungrouped.
Reactions on a specific image
When a recipient taps back on one image in a carousel, the message.reaction
webhook tells you exactly which one. The payload includes:
part_index— the 0-based position of the reacted item within the message.reacted_attachment— the exact attachment that was reacted to:{ index, url, media_type, size, caption }. It'snullwhen the tapback landed on the caption/text or a non-multipart message.
So a love on the 4th photo of a carousel reports part_index: 3 and a
reacted_attachment pointing at that specific image — not just "loved the
message". The full ordered attachments array is included on the event too.
Attribution badge (iMessage)
Set the optional badge field on a v4 send to show an attribution line under the
bubble, the way a message dictated to Siri or sent during a FaceTime call appears:
badge value |
Recipient sees |
|---|---|
sent_with_siri |
Sent with Siri |
sent_with_facetime |
Sent with FaceTime |
Omit badge to send no attribution. Any other value returns 422 invalid_content.
curl -X POST 'https://api.blooio.com/v4/channels/CHANNEL_ID/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"to": "+15551234567",
"text": "On my way!",
"badge": "sent_with_siri"
}'Try itIt works on text, attachments (media), parts (multipart), rich_link, and
app_clip sends, and the badge is rendered on the recipient's device.
Note
badgeis Blooio iMessage only. Setting it on any other content type (poll, interactive, invitation, imessage_app) returns422 invalid_content; it has no effect on non-Blooio channels.
Best practices
- Host media on stable, publicly reachable URLs (HTTPS)
- Prefer reasonably sized images to reduce delivery latency
- Include
metadatato correlate messages in your system