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 it

Node.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: true on another channel type returns 422 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 it

To 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: false to opt out and send separate bubbles. Setting carousel: true explicitly 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's null when 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 it

It works on text, attachments (media), parts (multipart), rich_link, and app_clip sends, and the badge is rendered on the recipient's device.

Note

badge is Blooio iMessage only. Setting it on any other content type (poll, interactive, invitation, imessage_app) returns 422 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 metadata to correlate messages in your system