Skip to main content
The BlueBubbles SDK exposes all attachment operations through client.attachments. You can upload files before embedding them in multipart messages, download attachments by GUID, force-download files not yet cached on the Mac, fetch metadata, generate blur hashes for progressive image loading, and retrieve live photo videos. Each method corresponds to an endpoint on your BlueBubbles Server.

Upload an attachment for multipart messages

When you want to include a file inside a multipart message, you must upload it first. The two-step flow is:
1

Upload the file

Call uploadAttachment() with a multipart form body containing your file. The server stores the file and returns a unique UUID you reference in the next step.
import { BlueBubblesClient } from "bluebubbles-sdk";
import { readFileSync } from "fs";

const client = new BlueBubblesClient({
BASE: "http://your-server:1234",
PASSWORD: "your-server-password",
});

const formData = new FormData();
formData.append(
  "attachment",
  new Blob([readFileSync("./photo.jpg")], { type: "image/jpeg" }),
  "photo.jpg"
);

const uploadResponse = await client.attachments.uploadAttachment({
  requestBody: formData,
});

// The UUID you need for the next step
const attachmentUuid = uploadResponse.data.hash;
The response shape looks like:
{
  "status": 200,
  "message": "Success",
  "data": {
    "hash": "a04a23a0833b33db8a63ee0816948786"
  }
}
2

Reference the UUID in a multipart message

Pass the hash value from the upload response as the attachment field in the corresponding part:
await client.messages.sendMultipartMessage({
  requestBody: {
    chatGuid: "iMessage;+;+15551234567",
    parts: [
      { partIndex: 0, text: "Here's the photo you asked for!" },
      {
        partIndex: 1,
        attachment: attachmentUuid,
        name: "photo.jpg",
      },
    ],
  },
});
This two-step upload flow is only required for multipart messages. If you just want to send a single file to a chat, use client.messages.sendAttachment() directly instead — no pre-upload needed.

Download an attachment

Use download() to retrieve the raw file bytes for an attachment you already have the GUID for. This is the standard download path — it works when the attachment is already cached on the Mac.
const fileData = await client.attachments.download({
  guid: "ATTACHMENT-GUID-HERE",
});

// fileData contains the raw binary response from the server
You can get attachment GUIDs from message query results. When you call client.messages.list() with "with": ["attachment"], each message object includes an attachments array with GUIDs you can pass directly to download().

Force-download an attachment

If an attachment exists in the database but hasn’t been downloaded to the Mac yet, download() returns a 404. In that case, use forceDownloadAttachment() to trigger the Mac to fetch the file from iCloud first.
forceDownloadAttachment() requires the Private API to be enabled on your BlueBubbles Server.
// Try the regular download first
try {
  const fileData = await client.attachments.download({
    guid: "ATTACHMENT-GUID-HERE",
  });
  // Handle file data
} catch (err) {
  // Fall back to force-download if the file isn't on the Mac yet
  const fileData = await client.attachments.forceDownloadAttachment({
    guid: "ATTACHMENT-GUID-HERE",
  });
  // Handle file data
}
Use forceDownloadAttachment() when building apps that need to reliably retrieve older messages. Attachments from messages received before the BlueBubbles Server was installed may not be locally cached.

Fetch attachment metadata

Use get() to retrieve the database record for an attachment without downloading the file. This is useful for checking the MIME type, filename, or size before deciding whether to download.
const attachment = await client.attachments.get({
  guid: "ATTACHMENT-GUID-HERE",
});

console.log(attachment.data.mimeType);   // e.g. "image/jpeg"
console.log(attachment.data.transferName); // e.g. "photo.jpg"

Generate a blur hash

getAttachmentBlurhash() generates a BlurHash string for an image attachment. You can use the hash to render a blurred placeholder in your UI before the full image loads.
Generating a BlurHash is CPU-intensive on the server. For large images, the server resizes internally before hashing. Expect slightly longer response times compared to other attachment endpoints.
const blurhashResponse = await client.attachments.getAttachmentBlurhash({
  guid: "ATTACHMENT-GUID-HERE",
});

const hash = blurhashResponse.data; // e.g. "LKO2?U%2Tw=w]~RBVZRi};RPxuwH"

// Use with the `blurhash` npm package to decode and render

Retrieve a live photo

Use getAttachmentLivePhoto() to retrieve the video component of a Live Photo. If the attachment is not an image or doesn’t have a live version, the server returns an error.
try {
  const livePhoto = await client.attachments.getAttachmentLivePhoto({
    guid: "ATTACHMENT-GUID-HERE",
  });
  // livePhoto contains the raw video bytes (typically a .mov file)
} catch (err) {
  // The attachment does not have a live photo component
  console.error("No live photo available for this attachment.");
}
Check the attachment’s MIME type with get() before calling getAttachmentLivePhoto(). Live photos are associated with image/jpeg or image/heic attachments that also have a paired video file.