> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# MMS

> Send picture and multimedia messages — single MMS and group MMS — via the Orbit API

# MMS

Send multimedia messages (JPEG, PNG, GIF, and WebP images, plus a set of carrier-accepted audio and video formats) to NANP (US/Canada) destinations. Each attachment is capped at **5 MB**. Orbit exposes two MMS surfaces:

1. **Single MMS** — `POST /api/v1/messages/sms` with `media_url` / `media_urls`. The SMS endpoint auto-upgrades the send to MMS as soon as one or more attachments are present.
2. **Group MMS** — `POST /api/v1/messages/group` fans a single payload (body + media) out to up to **20** recipients in one call.

<Note>
  **Coverage:** MMS is delivered to **NANP (US/Canada, E.164 `+1`) numbers only.** Non-NANP recipients are rejected per-message with `MMS_NANP_ONLY`. For non-NANP destinations send picture content as a [WhatsApp](/channels/whatsapp) or [RCS](/channels/rcs) message instead.
</Note>

## Single MMS

There is no dedicated single-MMS endpoint — send through the [SMS endpoint](/channels/sms) and attach media. When `media_url` or a non-empty `media_urls` array is present, the message is automatically upgraded from SMS to MMS by the Telnyx MMS provider.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messages/sms \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155552671",
    "from": "+18005551234",
    "body": "Check out our new store!",
    "media_urls": [
      "https://storage.googleapis.com/your-bucket/flyer.jpg",
      "https://storage.googleapis.com/your-bucket/map.png"
    ]
  }'
```

### Media fields

| Field        | Type      | Notes                                                                                             |
| ------------ | --------- | ------------------------------------------------------------------------------------------------- |
| `media_url`  | string    | A single attachment URL.                                                                          |
| `media_urls` | string\[] | Up to **10** attachment URLs. Both fields may be combined; together they upgrade the send to MMS. |

* **Body is optional** when at least one attachment is present — an MMS may be media-only.
* Each URL must be **HTTPS** and resolve through the send-time SSRF allowlist (e.g. `storage.googleapis.com`). URLs that fail the allowlist or DNS-rebind check are rejected before dispatch.
* Up to **10 attachments** per message are forwarded to the provider.

### Supported media types and size limit

Each attachment is content-type-checked and size-checked at send time (Orbit issues an HTTPS `HEAD` request against the URL). Attachments outside this allowlist, or larger than **5 MB**, are rejected before the message is dispatched — the carrier MMSC and the underlying provider reject them, so the send fails fast with a `422` rather than silently dropping the media.

| Category | Accepted content types                               |
| -------- | ---------------------------------------------------- |
| Image    | `image/jpeg`, `image/png`, `image/gif`, `image/webp` |
| Video    | `video/mp4`, `video/3gpp`                            |
| Audio    | `audio/mpeg`, `audio/ogg`, `audio/aac`               |

* **Maximum size:** 5 MB per attachment (hard limit). For the most reliable delivery across carriers, keep each attachment under **1 MB** — larger images are frequently down-rendered or dropped by individual carrier gateways even under the 5 MB ceiling.
* **Not supported:** documents (PDF), contact cards (vCard), and other content types outside the table above. To deliver those, link to an HTTPS-hosted file in the message body, or send the content over [WhatsApp](/channels/whatsapp) or [RCS](/channels/rcs) instead.

### Response

```json theme={null}
{
  "data": {
    "id": "msg_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
    "status": "queued",
    "channel": "mms",
    "segments": 1
  },
  "meta": {
    "request_id": "req_xyz789",
    "timestamp": "2026-06-08T00:00:00Z"
  }
}
```

## Group MMS

`POST /api/v1/messages/group` delivers a single MMS payload to up to **20** recipients in one operation (Twilio Notify / Bandwidth group-MMS parity). Each recipient is pre-inserted as a `pending` row before the per-recipient send pipeline runs, so a pre-send rejection (NANP gate, validation, fraud, quota, sender-validation, media SSRF, compliance) flips that recipient's row to `failed` rather than dropping it silently.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messages/group \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+18005551234",
    "to": ["+14155552671", "+14155552672", "+16505553000"],
    "body": "Weekend sale — 20% off everything!",
    "media_urls": ["https://storage.googleapis.com/your-bucket/sale.jpg"]
  }'
```

### Request body

| Field          | Type                   | Required    | Notes                                                                                             |
| -------------- | ---------------------- | ----------- | ------------------------------------------------------------------------------------------------- |
| `to`           | string\[]              | yes         | 1–20 E.164 recipients. Duplicates in the same request are rejected.                               |
| `from`         | string                 | no          | MMS-capable sender DID. Resolved through the standard sender chain when omitted.                  |
| `body`         | string                 | conditional | 1–1600 chars. At least one of `body` or `media_urls` must be present.                             |
| `media_urls`   | string\[]              | conditional | Up to 10 HTTPS attachment URLs (SSRF-allowlisted at send time).                                   |
| `scheduled_at` | string (ISO 8601)      | no          | Batch-level scheduling — every recipient lands as `scheduled` and the worker queue picks them up. |
| `webhook_url`  | string                 | no          | One-off DLR webhook URL for this group send.                                                      |
| `metadata`     | object (string→string) | no          | Shared metadata merged onto every recipient row.                                                  |

<Note>
  **Cap:** 20 recipients per call. The per-leg idempotency key is `group-mms:{group_id}:{to}`. For larger sends use the [batch endpoint](/channels/sms) or the campaigns module.
</Note>

### Response — 207 Multi-Status

The group endpoint always returns a per-recipient `messages` array and a `summary`. The HTTP status is:

* **200 OK** — every recipient succeeded.
* **207 Multi-Status** — any recipient failed (partial *or* total failure). Inspect the body for per-recipient `status` / `error_code`.
* **402 Payment Required** — the org balance cannot cover the group floor (pre-flight check).
* **500** — bulk pre-insert failure (no recipients dispatched).

```json theme={null}
{
  "group_id": "batch_grp_abc123",
  "messages": [
    { "message_id": "msg_0f1e2d3c4b5a69788796a5b4c3d2e1f0", "to": "+14155552671", "status": "sent" },
    { "message_id": "msg_112233445566778899aabbccddeeff00", "to": "+14155552672", "status": "sent" },
    {
      "message_id": "msg_ffeeddccbbaa99887766554433221100",
      "to": "+16505553000",
      "status": "failed",
      "error_code": "MMS_NANP_ONLY",
      "error_message": "MMS is only available for NANP (US/Canada) recipients"
    }
  ],
  "summary": {
    "total": 3,
    "succeeded": 2,
    "failed": 1,
    "scheduled": 0
  }
}
```

## Message Status Flow

`queued` → `sending` → `sent` → `delivered` (or `failed` / `undelivered`)

Subscribe to status updates via [webhooks](/webhooks/overview) using the `message.sent`, `message.delivered`, and `message.failed` events. For group MMS, one event fires per recipient, each carrying its own message `id`.

## Rate Limits

| Surface                            | Limit                                            |
| ---------------------------------- | ------------------------------------------------ |
| Group MMS (`POST /messages/group`) | 30 requests / minute per tenant                  |
| Single MMS (`POST /messages/sms`)  | Same per-plan throughput as [SMS](/channels/sms) |

## Outbound provider

Outbound MMS is delivered via **Telnyx**, the named exception to Orbit's outbound-termination policy: voice and SMS exit only via the Devotel softswitch, but MMS (alongside fax/T.38) outbound may use Telnyx because the softswitch does not support MT MMS. Orbit routes all MMS — single and group — through the standard messaging router; there is no provider bypass.

## Pricing

MMS is billed per message (not per attachment) and varies by destination. Check the [pricing page](https://orbit.devotel.io/pricing) or query the `GET /api/v1/pricing/messaging?country=<ISO>` endpoint for real-time rates (the returned `channels` array includes `mms`).
