MMS media rich-content cookbook
Every recipe here runs against the MMS surfaces end to end — single sends throughPOST /api/v1/messages/sms, group sends through POST /api/v1/messages/group — so you can copy the exact request and response shapes your integration handles. The MMS send & receive guide walks the dashboard and API from first principles; the MMS channel reference lists every request field and cap. This page is the cookbook: sizing, patterns, fallbacks, and failure handling for media traffic.
Authenticate every request with X-API-Key. Run against the sandbox with a dv_test_sk_… key, then swap in your live key.
Task index
1. Quick send — one MMS, one image
Send through the SMS endpoint with amedia_urls array — the send auto-upgrades to MMS as soon as one attachment is present:
cURL
Node.js
"channel": "mms" on the returned message. Always send an Idempotency-Key on single sends — a retried request returns the original message instead of dispatching a duplicate.
The media_urls array is additive with the singular media_url field — combine them or use either; together they carry up to 10 attachment URLs.
2. Size media for carrier acceptance
The platform enforces a 5 MB ceiling per attachment and rejects anything larger with422 VALIDATION_ERROR before dispatch. Carriers enforce their own, much tighter, transcode budgets downstream — size your assets for the carriers, not the ceiling.
Accepted content types (anything outside this table is rejected with
422):
Media URLs must be HTTPS and resolve through the send-time URL allowlist — the platform issues a
HEAD request per URL to check type and size, and rejects URLs that fail allowlist or DNS-rebind checks before dispatch. Documents (PDF), vCards, and formats outside the table are not deliverable attachments; recipe 4 covers the contact-card path and recipe 5 the link-based fallback.
3. Group send — one payload, up to 20 recipients
POST /api/v1/messages/group fans one body + media_urls payload out to up to 20 recipients, with a per-recipient status breakdown in the response:
cURL
- Delivered-to semantics. Each recipient gets an independent MMS — this is one-to-many fan-out, not a shared group thread. Recipients cannot see or reply to each other.
- Partial failure. The endpoint returns
200 OKwhen every recipient succeeded and207 Multi-Statuson any failure. Read the per-recipientstatus/error_codein the body, then retry only the failed recipients (see recipe 7). - Pre-filter non-NANP recipients. MMS delivers to US/Canada
+1numbers only; a non-+1recipient comes back failed withMMS_NANP_ONLY. Filter before dispatch with the partition pattern on the channel reference to avoid paying for known non-deliverables. - Cap and shape. 20 recipients per call,
body1–1600 characters, up to 10 attachments, sharedmetadatamerged onto every recipient row. For larger broadcasts use the batch SMS endpoint (with the same media upgrade) or the campaigns module. - Opt-out caveats. Orbit is the conduit — consent and opt-out handling are your tenant-side controls. Suppression lists and quiet hours are tenant-configurable under Settings → Compliance; group sends honor the suppression list, so a suppressed recipient lands as a visible
failedrow rather than a silent drop. Keep your opt-out keywords and suppression source synced before a campaign.
4. Hand off a contact card
MMS does not accept vCard attachments — atext/vcard URL is outside the content-type allowlist and is rejected with 422 VALIDATION_ERROR before dispatch, the same as PDFs and other documents. Two shipped paths deliver contact details to a handset:
- Host the vCard, send the URL. Write the
.vcffile to HTTPS storage and put the link in the MMS body. Most handsets render the URL tappable; the recipient downloads and saves the card. Make it a tracked short link (recipe 5) and each save shows up as a click. - Send a native contacts message on WhatsApp. WhatsApp carries first-class contact messages (full vCard payload rendered as an in-app card). If your recipients are reachable on WhatsApp, route the contact card through the WhatsApp channel instead — no tap-through required.
5. SMS fallback with a tracked short link
Rich content over MMS stops at three boundaries: non-NANP recipients, media above what carriers accept, and content types outside the media table. In each case the fallback is a plain SMS carrying a tracked short link to the hosted image — recipients tap through, and you keep click analytics on the handoff. Because shortening is per-URL fire-and-forget, a failed mint never blocks the send — the original URL ships untouched.cURL
MMS_NANP_ONLY on some recipients, re-send those recipients on this pattern — switch the surface, do not retry MMS. For destinations reachable on WhatsApp or RCS, inline media there beats a tap-through link.
The full pattern — inline shortening, net-shortening guard, per-campaign rollups, per-contact clicks, and the short_link.click webhook — is in Short links with click tracking and the short-links cookbook.
6. Personalize body text per recipient, share one media set
One media URL set (or one store-to-uploads upload) serves the whole audience; vary only thebody per recipient.
Per-recipient sends in a loop. Render the body from a template in your application, then send one request per recipient:
Node.js
Idempotency-Key per recipient so a retried loop never double-sends.
Campaign-style sends. On a campaign, the merge-tag layer renders the body per recipient at send time against the campaign’s message_template. The five contact fields ({{first_name}}, {{last_name}}, {{phone}}, {{email}}, {{company}}), plus {{coupon_code}} and every key on campaign.variables, resolve per recipient — anything unresolved renders blank with no send-time error, so run the dry-run and clear every unresolved tag before launch. The contract and pre-launch validation flow are in Validate campaign personalization merge-tags before launch.
7. Handle the media error surface
Media sends fail in four classes, each with a different playbook: Pre-send rejection (422 VALIDATION_ERROR). The media HEAD check failed — wrong content type, over 5 MB, or a URL outside the HTTPS allowlist. Read details.issues on the response, fix the attachment, and retry with a fresh Idempotency-Key. Nothing was dispatched.
Destination rejection (MMS_NANP_ONLY). Non-+1 recipient, rejected per message at send time. Not retryable — switch the recipient to WhatsApp/RCS or the SMS-with-link pattern (recipe 5).
Post-dispatch carrier rejection (undelivered / failed). Dispatch succeeded but the downstream carrier or handset refused the media — most often an oversize attachment or a carrier filter. One message.failed webhook event fires per recipient carrying your media_urls; re-send on SMS-with-link (recipe 5) and shrink the asset under the 300 KB carrier-acceptance band before trying MMS again. The carrier classes and their playbooks are in the undelivered / failed message guide.
Mixed fan-out (207 Multi-Status). On a group send some recipients failed while others dispatched. Read the per-recipient error_code in the response body, split the failures into the classes above, and re-drive only retryable ones — never blind-retry the whole group.
Codes, HTTP statuses, and remediation for every failure surface above are catalogued in the error code reference.
8. Budget for MMS pricing
MMS bills per message, not per attachment — a message with three attachments costs one MMS message, and MMS is a richer (higher-priced) surface than SMS. On a group send each recipient bills as its own message; the dashboard composer shows a cost estimate before you confirm. Rates vary by destination. Check the pricing page or query real-time rates:cURL
channels array includes mms alongside sms. Fall back to SMS-with-link (recipe 5) when the media does not justify the richer per-message rate.
Next steps
- MMS channel reference — every request field, cap, and response shape
- MMS send & receive — dashboard + API quickstart on the same surfaces
- Short links with click tracking — the fallback pattern’s full mechanics
- Error code reference — every code this page names, with remediation