MMS composition model
Orbit MMS is not a separate messaging engine — it is an extension of the send pipeline that adds an attachment pipeline around the ordinary message body. This page owns the concept behind both MMS surfaces (single sends throughPOST /messages/sms and group sends through POST /messages/group): how a message is composed, how each attachment is validated and optionally re-encoded before dispatch, and how to read the rejection taxonomy. The step-by-step send recipe stays in the API recipes cookbook; the full field table stays in the MMS channel reference.
Composition shapes
An MMS carries any of three compositions, and at least one of the two ingredients must be present:- Body + media — the ordinary shape: text plus up to 10 attachment URLs.
- Media-only — no body. The send upgrades to MMS purely because an attachment exists.
- Group envelope — one payload (body, media, or both) fanned out to up to 20 recipients in a single call, with per-recipient rows so individual pre-send rejections never drop a recipient silently.
POST /messages/sms auto-upgrades the send to MMS the moment media_url or a non-empty media_urls array is present, and the response reports channel: "mms". Treat MMS composition, therefore, as something the pipeline derives from the presence of attachments — not a channel you select.
The attachment pipeline
Every attachment URL passes through the same three-step pipeline before a carrier ever sees it:- URL gates (fail fast). Each URL must be HTTPS and must resolve through the send-time SSRF allowlist. URLs that point at internal addresses, fail the allowlist, or break the DNS-rebind check are rejected before dispatch, and no carrier call is attempted.
- HEAD-preflight assessment. Orbit issues an HTTPS
HEADrequest against the URL and classifies it from the cheapContent-Type+Content-Lengthsignals into one of three carrier outcomes:ok— a carrier-native type (JPEG, PNG, GIF, WebP images; MP4/3GPP video; MPEG/OGG/AAC audio) under the recommended soft cap of 1 MB. Sent as-is.optimize— a carrier-native type between the 1 MB soft cap and the 5 MB hard ceiling. The send is accepted, and an advisory is stamped onto the message metadata flagging which attachments a carrier may downscale or drop.reject— un-sendable as-is: either a content type no carrier MMSC accepts, or a payload over 5 MB. The rejection names the carrier-native format to convert to, so the fix is concrete rather than a flat allowed-types dump.
- Optional re-encode. Non-canonical image types (HEIC, HEIF, BMP, TIFF) and oversized images can be re-encoded to a carrier-native image before dispatch instead of being rejected — either inline on the send path or ahead of time through the asynchronous
POST /messages/media/convertendpoint (pollGET /messages/media/convert/:jobIduntilcompleted, then send with the returned URL). Images only: video and audio conversion is not supported, and SVG is never rasterized server-side. The re-encode changes bytes, never transport — outbound MMS still exits via the named Telnyx exception to Orbit’s outbound-termination policy, exactly as documents (PDFs) or contact cards (vCards) remain out of scope entirely.
The rejection taxonomy
MMS failures cluster into a small, decision-grade taxonomy. Branch on the error class, not the message text:
Two invariants make this taxonomy safe to build on: a rejected MMS never silently falls back to SMS as text-only, and group fan-out never aborts the whole batch on one bad recipient — a failure lands on that recipient’s row alone.
Group MMS shape
Group MMS is a composition-time fan-out, not a campaign. The caps are enforced in one step:- 20 recipients maximum per call (duplicates rejected at
422). Beyond 20, chunk or move to the campaign or batch surfaces. - Per-recipient rows. Every recipient is pre-inserted as a
pendingrow before the per-recipient pipeline runs, so a pre-send rejection (coverage, media, quota) flips that one row tofailedinstead of dropping it. - Per-leg idempotency. Recipients dedupe under the key
group-mms:{group_id}:{to}, so a retried group call replays cleanly instead of double-sending. - Status shape.
200when every recipient succeeded;207with a per-recipientmessagesarray when any failed. Readerror_codeper recipient rather than relying on the envelope status.
Where MMS sits in the delivery lifecycle
Once an attachment passes the pipeline, the message joins the shared destination lifecycle every messaging channel uses — the same state machine, the same webhook vocabulary, the same debugging surfaces. MMS is an SMPP-backed channel, so the lifecycle details that matter most to reporting are the SMPP-backed ones:- The no-receipt grace window is 30 minutes. Thirty minutes after a provider accept, a message with no delivery receipt moves from
senttosubmitted_no_receipt. submitted_no_receiptfor MMS is genuinely ambiguous — the handset may have received the media with no receipt reported, or the carrier may never emit receipts on that route. It is a wire-intermediate state (is_terminal: false), not a delivery signal. Track it separately from your delivered rate; a rising share on one destination points at a non-cooperating route.- Telnyx returns carrier-style receipts for MMS legs, so a
deliveredstate is a carrier-confirmed outcome exactly as it is for SMS.
id — diagnose them per recipient, not as a batch. The state-machine concept is owned by Delivery lifecycle; the transition table by the message status lifecycle reference.
Choosing MMS — and its buddy channels
MMS is the right surface for inline image, video, or audio to US/Canada mobile numbers. When a recipient sits outside NANP, or the content is a document type no MMSC accepts, MMS’s rejection is a routing decision input — pick the buddy channel and move on:- Non-NANP picture/video recipients — WhatsApp or RCS carry rich content internationally.
- Documents and contact cards — link an HTTPS-hosted file in the SMS or WhatsApp body rather than trying to attach it.
sms, whatsapp, rcs, viber, telegram, messenger, instagram, line, apple_messages), any of the five fallback and cascade planes can move a rejected or undeliverable MMS onto a buddy channel — from an org-wide chain to a per-send fallback_channels field. MMS itself is a first-class internal channel surface, but the fallback machinery treats the hop as a fresh send on the buddy channel with the same consent, quiet-hours, and billing gates every hop of every plane re-runs.
Related
- MMS channel — endpoint reference, media table, caps, rate limits
- MMS recipe — the send → webhook → error-branch flow, end to end
- Media planes — the realtime voice/video planes (not the attachment pipeline)
- Fallback and cascade planes — moving a rejected MMS onto a buddy channel
- Delivery lifecycle — the shared status machine and the SMPP-backed 30-minute window
- SMS channel — the single-send surface MMS auto-upgrades from