
iMessage delivers photos, videos, audio clips, documents, and other files alongside text. If you are building an integration that needs to handle those attachments, here is the practical picture: the current Figment public REST API covers message text and identifiers—there is no published attachment-download route in the current contract. This guide explains attachment availability on the connected Mac, then gives you the scoped download design, type filter, and failure matrix you need to build a solid integration layer.
The GET /v1/connections/{connection_id}/messages endpoint returns message items with these documented fields: guid, text, isFromMe, and chatGuid. Both history reads and search operate under messages:read. No attachment schema or download route is published in the current API contract.
That boundary does not prevent you from designing your integration now. The pattern below applies whether you are building a Mac-side adapter over the local message store, evaluating a future dedicated endpoint, or adapting another bridge. The failure states and filter logic are the same regardless of the source.
iMessage stores attachment files in the Mac's local Messages database alongside a small metadata record for each file. When Messages in iCloud is enabled, those attachments sync from the user's devices. The sync state at the moment of access matters: a database record can reference a file whose payload is not currently on disk.
Four states drive every downstream decision:
| State | What it means | Retryable |
|---|---|---|
| Available | File is on disk; payload is accessible | — |
| Downloading | iCloud sync for this file is in progress | Yes — defer |
| Offloaded | File removed from local disk to free space; record remains | Yes — defer after sync |
| Deleted | Removed by the user; no payload available | No |
iMessage is permissive about content types: you can receive images, video, audio, PDFs, .vcf contact cards, and .ics calendar invites. Apple has not published an exhaustive allowed-type list. SMS and MMS attachments follow separate carrier-imposed size limits, which vary by carrier and are typically much lower than iMessage's practical ceiling.
Before requesting any file payload, run a filter pass over the metadata you already have. This avoids queuing large downloads for an integration that only needs PDFs, and it surfaces a clear reason for every skip.
# Integration design — not a live Figment endpoint.
# Adapt field names and state values to match your actual data source schema.
DOWNLOAD_LIMIT_BYTES = 25 * 1024 * 1024 # Application-level policy; set for your budget
ACCEPTED_TYPES = {
"image/jpeg", "image/png", "image/heic",
"video/mp4", "video/quicktime",
"audio/m4a",
"application/pdf",
}
def classify_attachment(att):
state = att.get("state") # local-availability field from your adapter
mime = att.get("mime_type", "")
size = att.get("total_bytes") # may be None during partial sync
if state == "deleted":
return "skip_deleted"
if state != "available":
return "defer_not_ready"
if mime not in ACCEPTED_TYPES:
return "skip_type"
if size is None:
return "defer_unknown_size"
if size > DOWNLOAD_LIMIT_BYTES:
return "skip_too_large"
return "download"
def process_attachments(message):
for att in message.get("attachments", []):
action = classify_attachment(att)
guid = att.get("guid", "")
if action == "download":
queue_download(guid)
else:
record_skip(guid, reason=action)
A few decisions baked in:
Deleted files are classified first. You learn the file is gone before any network call.
Unknown size defers, not errors. Metadata gaps happen during partial sync. Record the skip and re-evaluate once the connection reports ready.
Type filtering is a product decision. What your integration accepts is up to you. A CRM integration might skip audio and video entirely; a media archive might accept everything under its size cap.
Size limit is configurable. 25 MB is an application-level policy, not an iMessage constraint. Set it to match your storage budget and processing pipeline. Large video files can arrive in bursts.
| Classification | Retryable | Recommended action |
|---|---|---|
skip_deleted | No | Record skip. Alert if business-critical. |
defer_not_ready | Yes | Re-check after services.messages.state = ready. |
skip_type | No | Record skip with MIME type logged. |
defer_unknown_size | Yes | Re-check after sync completes. |
skip_too_large | No | Record skip with declared size logged. |
Do not trigger a blind re-fetch on a failed or deferred outcome. Apply the same principle from the iMessage send model: an unknown outcome should poll, not re-request blindly.
Confirm the Messages service is ready before trusting query results:
GET https://figment.so/api/apple-connect/v1/connections/{connection_id}
Authorization: Bearer ACCESS_TOKEN
The response shape is {connection:{id,mode,services,...}}. Check connection.services.messages.state. A ready value means the service is active—it does not prove all historical attachments have finished syncing. Use the historyComplete flag on message list responses as the specific signal for complete history.
Non-ready states (syncing, needs_approval, reconnect_required) mean operations are rejected. Wait until ready before treating "no attachments found" as authoritative.
iMessage attachments arrive through Apple's protocol because the connected Mac holds the message database. SMS, MMS, and RCS messages reach the Mac only when an iPhone forwards them.
Apple's forwarding guide (support.apple.com/en-us/102545) describes two paths:
If the connected iPhone is unavailable or forwarding is not configured, SMS and MMS messages—and their attachments—will not appear even if the Apple Account is signed in on the Mac. Use the service field on the message record to distinguish iMessage from SMS/MMS traffic, and apply separate size policies for MMS.
Attachment handling uses messages:read—the same scope that covers message history and search. No separate attachment scope exists in the current API. messages:send is a distinct scope required only for sending; a read-only integration such as an archivist, CRM enrichment pipeline, or media processing agent does not need it.
The developer price is $29/month per active live connection; sandbox access is free. Access starts after setup.
Register your app and connect an Apple Account to request a scoped grant. Manage existing grants at /apple-connect/account.
Related: