Skip to main content
Send multimedia messages (images, video, audio, vCards) via the Telnyx API and process inbound MMS attachments from webhooks.

Prerequisites

MMS is supported on US/Canada long codes, toll-free, and short codes. For media format details and carrier limits, see MMS Media & Transcoding.

Send an MMS

Include media_urls in your message request. You can send up to 10 media files per message.
import telnyx

telnyx.api_key = "YOUR_API_KEY"

message = telnyx.Message.create(
    from_="+18005550100",
    to="+18005550101",
    text="Here's the photo you requested!",
    media_urls=["https://example.com/image.jpg"],
    messaging_profile_id="YOUR_MESSAGING_PROFILE_ID"
)

print(f"Message ID: {message.id}")
print(f"Status: {message.to[0]['status']}")

Send multiple media files

Include multiple URLs in media_urls. Total payload must stay under carrier limits.
curl
curl -X POST https://api.telnyx.com/v2/messages \
  -H "Authorization: Bearer $TELNYX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+18005550100",
    "to": "+18005550101",
    "text": "Product photos attached",
    "media_urls": [
      "https://example.com/photo1.jpg",
      "https://example.com/photo2.jpg",
      "https://example.com/photo3.jpg"
    ],
    "messaging_profile_id": "YOUR_MESSAGING_PROFILE_ID"
  }'
Media URLs must be publicly accessible. Telnyx downloads the media at send time — if the URL requires authentication or returns an error, the message will fail.

Receive an MMS

Inbound MMS messages arrive as webhooks to your messaging profile’s webhook URL. The media array contains attachment details.

Webhook payload

{
  "data": {
    "event_type": "message.received",
    "payload": {
      "from": { "phone_number": "+18005550101" },
      "to": [{ "phone_number": "+18005550100" }],
      "text": "Check out this photo!",
      "media": [
        {
          "url": "https://media.telnyx.com/abc123/image.jpg",
          "content_type": "image/jpeg",
          "size": 245760
        }
      ]
    }
  }
}
Media URLs are ephemeral. Telnyx-hosted media links expire. Download and store attachments in your own storage immediately upon receipt.

Process inbound MMS

from flask import Flask, request, jsonify
import requests
import os

app = Flask(__name__)

TELNYX_API_KEY = os.getenv("TELNYX_API_KEY")
MEDIA_DIR = "./received_media"
os.makedirs(MEDIA_DIR, exist_ok=True)

@app.route("/webhooks", methods=["POST"])
def webhooks():
    body = request.json
    event_type = body["data"]["event_type"]

    if event_type != "message.received":
        return jsonify({"status": "ignored"}), 200

    payload = body["data"]["payload"]
    from_number = payload["from"]["phone_number"]
    text = payload.get("text", "")
    media = payload.get("media", [])

    print(f"From: {from_number} | Text: {text} | Attachments: {len(media)}")

    # Download each attachment
    saved_files = []
    for item in media:
        resp = requests.get(item["url"])
        ext = item["content_type"].split("/")[-1]
        filename = f"{MEDIA_DIR}/{from_number}_{len(saved_files)}.{ext}"
        with open(filename, "wb") as f:
            f.write(resp.content)
        saved_files.append(filename)
        print(f"  Saved: {filename} ({item['size']} bytes)")

    return jsonify({"status": "ok", "files": len(saved_files)}), 200

if __name__ == "__main__":
    app.run(port=8000)

Reply with media

Echo received media back to the sender, or reply with different media:
import telnyx
import os

telnyx.api_key = os.getenv("TELNYX_API_KEY")

def handle_mms_webhook(payload):
    """Reply to inbound MMS with the same media + a text response."""
    from_number = payload["from"]["phone_number"]
    to_number = payload["to"][0]["phone_number"]
    media = payload.get("media", [])

    # Reply with the same media echoed back
    media_urls = [item["url"] for item in media]

    reply = telnyx.Message.create(
        from_=to_number,
        to=from_number,
        text=f"Thanks! Received {len(media)} attachment(s).",
        media_urls=media_urls if media_urls else None,
        messaging_profile_id="YOUR_MESSAGING_PROFILE_ID"
    )

    print(f"Reply sent: {reply.id}")

Supported media types

TypeFormatsMax Size
ImagesJPEG, PNG, GIF, BMP, WebP1 MB (carrier-dependent)
VideoMP4, 3GP600 KB (carrier-dependent)
AudioMP3, AMR, WAV, OGG600 KB (carrier-dependent)
FilesvCard (.vcf), PDF600 KB
Telnyx automatically transcodes oversized media when possible. For details on carrier-specific limits and transcoding behavior, see MMS Media & Transcoding.

Store media externally (optional)

For production use, store received media in your own cloud storage rather than relying on ephemeral Telnyx URLs.
Python
import boto3
import requests
from urllib.parse import urlparse

s3 = boto3.client("s3")
BUCKET = "your-mms-bucket"

def save_to_s3(media_url, from_number, index):
    resp = requests.get(media_url)
    content_type = resp.headers.get("content-type", "application/octet-stream")
    ext = content_type.split("/")[-1]
    key = f"mms/{from_number}/{index}.{ext}"

    s3.put_object(
        Bucket=BUCKET,
        Key=key,
        Body=resp.content,
        ContentType=content_type
    )
    return f"s3://{BUCKET}/{key}"
Python
from google.cloud import storage
import requests

gcs = storage.Client()
bucket = gcs.bucket("your-mms-bucket")

def save_to_gcs(media_url, from_number, index):
    resp = requests.get(media_url)
    content_type = resp.headers.get("content-type", "application/octet-stream")
    ext = content_type.split("/")[-1]

    blob = bucket.blob(f"mms/{from_number}/{index}.{ext}")
    blob.upload_from_string(resp.content, content_type=content_type)
    return blob.public_url

Troubleshooting

Cause: The media URL was unreachable, or the recipient’s carrier doesn’t support MMS.Fix:
  • Verify the media URL is publicly accessible (no auth required)
  • Check message detail records for error details
  • Confirm the recipient’s number supports MMS
Cause: Total media payload exceeds carrier limits.Fix:
  • Compress images before sending (aim for < 600 KB each)
  • Enable automatic transcoding (on by default)
  • See carrier size limits
Cause: Telnyx media URLs are temporary. You waited too long to download.Fix: Download media immediately in your webhook handler. Store in your own S3/GCS bucket.
Cause: Some number types (e.g., alphanumeric sender IDs) don’t support MMS.Fix: Use a US/Canada long code, toll-free, or short code with MMS enabled in your messaging profile.

Next steps