---
title: Send and receive pictures in SMS (MMS)
---
# Send and receive pictures in SMS (MMS)

## What it is

Omniscio can **send and receive pictures in a text** over the **Pushbullet** bridge
(Omniscio mirrors your Android phone's texts through Pushbullet). Pictures ride on your
real phone number — this is **not** a separate texting service like Twilio, so to
the other person a picture looks exactly like one you sent or received yourself.

> **Pushbullet only.** Pictures (MMS) work only on the **Pushbullet** provider. The
> native (self-hosted phone) provider is **text-only for now** — it can't send or
> receive pictures.

## Where to find it

In the SMS panel — the **New SMS** box or a conversation's reply box — use the picture button, or paste an image straight into the message box.

## How it behaves

### Sending a picture

In the SMS panel — either **New SMS** (pencil) or inside a conversation's reply
box:

1. Click the **picture button** (image icon) in the message bar and pick an
   image, **or** paste a screenshot directly into the message box (Ctrl+V).
2. A small **thumbnail preview** appears; remove it with the × if you change your
   mind. You can add a caption, or send the picture with no text at all.
3. Press **Send**. Omniscio shrinks the image to a carrier-friendly size, uploads it,
   and sends it attached to the text.

Works on **desktop and mobile**. (Scheduling a picture for later isn't supported —
Send Later stays text-only for now.)

### Receiving a picture

When someone texts **you** a picture, it shows up **inline in the conversation**,
the same as a picture you sent — click it for a fullscreen view. If they send
**several pictures in one text**, all of them appear as a small grid, and each one
opens fullscreen on its own when tapped. Omniscio fetches the images from Pushbullet
automatically in the background, so a picture-text may show its caption first and
then fill in the pictures a moment later.

If a picture genuinely can't be fetched (a rare network hiccup, or the picture is
old enough that Pushbullet no longer has it), the message falls back to a
**"📷 Photo — open on your phone"** note. Omniscio also **backfills** received pictures
already in your history — a few per sync so it never floods — so older
picture-texts light up over the first little while too.

> Heads-up for anyone who read the old version of this page: Omniscio **used to** say
> receiving pictures was impossible and needed a Twilio number. That was wrong —
> Pushbullet does hand Omniscio the image; Omniscio just wasn't reading it. It does now.

### Cost / heads-up

Picture messages count against your **Pushbullet** texting allowance and may
require **Pushbullet Pro** depending on your plan — worth a quick check before
relying on it for volume. Receiving doesn't send anything, so it doesn't use the
texting allowance; it just downloads the image Pushbullet already has.

## For agents

### Under the hood (for agents)

- **Outbound flow:** `IPC SMS_SEND { phoneNumber, message, imageDataUrl? }` →
  `sendOperatorSms` → `sendSms` → Pushbullet `POST /v2/upload-request` → multipart
  upload → `POST /v2/texts` with `file_type` **inside** `data` and `file_url` a
  **sibling**.
- **Inbound flow:** the thread-detail sync (`syncThreadDetail`) reads the
  `image_urls` array on an incoming `type: "mms"` message, fetches **every** allowed
  image in order (SSRF-guarded per image: https-only, Pushbullet/Backblaze hosts,
  size-capped, real-image-types only — see `sms-mms-image.ts`), and stores the
  ordered set on the row via `updateMessageMediaList`. Bounds: a per-message count
  cap + cumulative byte budget, plus a per-cycle IMAGE budget reserved
  whole-message-or-skip and a process attempt-guard, so a first-run history backfill
  never stampedes and a big batch can't bloat a row.
- Both directions store the picture(s) as bounded `data:` URLs on the message row —
  the full set as a JSON array in `sms_messages.media_json` (additive migration),
  with the legacy `media_data_url` / `media_mime` mirroring the first for back-compat;
  mirroring the contact-photo convention, not a media protocol handler.
- **Renderer:** `SmsViewer` reads the picture list through the `smsMessageImages`
  selector (normalized `media`, else the legacy single field) and renders one image
  exactly as before or a grid for two-plus, each with its own lightbox
  (direction-agnostic); the "open on your phone" placeholder shows only for an
  empty-body message with no stored picture.
- Full invariants + the security/shape decisions:
  `.claude/memory/contracts/sms-mms-pictures-contract.md`.

## Related

Emoji and GIFs in SMS is the sibling capability in the same composer, and scheduling a text is the third control that sits beside them.
