Skip to main content
Attachments use a two-step upload flow:
  1. Create an upload slot.
  2. PUT the bytes to upload_url.
  3. Send a chat message with a media part through POST /api/v1/chats/{id}/messages.
To send audio as a native iMessage voice-note bubble, use the separate Voice notes workflow. Do not upload it here or put a voice_note_id in attachments[]; generic audio attachments may render as ordinary files or media attachments. You can submit an ordinary uncompressed WAV to the voice-note endpoint; Chert performs the compatible Opus-in-CAF conversion server-side.

1. Create an upload slot

2. Upload bytes

HMAC is not supported on the binary upload route. Use bearer auth.
Single PUTs accept up to 100 MB per file. Chert streams the body straight through to storage — there is no in-memory buffering bottleneck, so large uploads scale with your network throughput. Set Content-Length on the PUT so we can fast-reject a body that exceeds the size you registered.

3. Send the attachment

Send multiple images as one bubble

For multiple uploaded media parts that should arrive as one grouped delivery rather than N back-to-back bubbles, send a normal chat message and set parallel_attachments: true. Chert requests grouped delivery; Apple devices decide the final collage, stack, or attachment presentation. The shape is the same parts[] you use for every other chat send. The only difference is the top-level parallel_attachments: true flag.
You can include parallel_attachments on the first touch too — POST /api/v1/chats accepts it inside message.

Rules

Errors

Supported uploads

The public API validates the declared file size and rejects unsupported MIME types. Upload slots expire after 15 minutes.

Metadata and download

Use GET /api/v1/attachments/{id} to check the registered attachment.
Use GET /api/v1/attachments/{id}/content to stream the binary content. This works for attachments you uploaded and for inbound media ids received in message.received webhooks.
Inbound images and files arrive in data.message.parts[] as media parts:
Inbound attachment metadata is best effort. A part may omit filename or size_bytes, and mime_type may be the generic application/octet-stream. The authenticated content response can provide a more useful filename in Content-Disposition; inspect the downloaded file signature and media metadata rather than trusting the webhook MIME type alone. Apple voice memos use this normal inbound attachment flow—there is no separate inbound voice-note endpoint. A verified recording downloaded as Audio_Message.caf, a playable CAF container with Opus audio, even though its event MIME type was application/octet-stream and the event did not include a filename. This observed CAF/Opus combination is not guaranteed across every device or OS version. See Receiving inbound voice memos.

Caveats

See also