> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getdial.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getdial.ai/_mcp/server.

# Send an SMS

> Send an SMS from a Dial phone number using the CLI, an SDK, or the REST API.

Send a text message from one of your Dial numbers to any phone number. You need two things: the recipient in E.164 format (e.g. `+14155550123`) and the `id` of the Dial number you're sending from (run [`dial number list`](/documentation/platform/manage-phone-numbers) to find it).

```bash title="CLI"
dial message \
  --to +14155550123 \
  --body "Hello from Dial" \
  --from-number-id pn_123
```

```typescript title="Node"
import { DialClient } from "@getdial/sdk";

const dial = new DialClient({ apiKey: process.env.DIAL_API_KEY! });
const message = await dial.sendMessage({
  to: "+14155550123",
  fromNumberId: "pn_123",
  body: "Hello from Dial",
});
console.log(message.id, message.status);
```

```python title="Python"
from dial_sdk import DialClient, DialConfig

dial = DialClient(DialConfig(api_key="sk_live_..."))
message = await dial.send_message(
    to="+14155550123",
    from_number_id="pn_123",
    body="Hello from Dial",
)
print(message.id, message.status)
```

```python title="LangChain"
from dial_langchain import SendMessageTool

send = SendMessageTool(api_key="sk_live_...")
await send.ainvoke({
    "to": "+14155550123",
    "from_number_id": "pn_123",
    "body": "Hello from Dial",
})
```

```bash title="cURL"
curl -X POST https://api.getdial.ai/v1/messages \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"to":"+14155550123","fromNumberId":"pn_123","body":"Hello from Dial"}'
```

The message comes back with an `id` and a `status` of `sent`, which advances to `delivered` (or `undelivered`/`failed`, with a `statusError` reason) as the carrier reports back. Track delivery and replies by [streaming events](/documentation/platform/stream-account-events) or listing messages (`GET /api/v1/messages`).

Accounts on a **subscription** can send SMS only to **US** numbers — any other destination is rejected with a `400`. Pay-as-you-go accounts can send to any supported destination.

## Attach media (MMS)

Attach up to 10 media items (5 MB each) to a message. Supply each one either as a **local file** — the CLI and SDKs upload it for you — or as a **public URL** that Dial downloads server-side. Either way Dial mirrors the bytes into its own storage and serves them from a stable public URL, returned in the message's `media` array.

**Supported types:** images (`jpeg`, `png`, `gif`, `webp`, `bmp`), audio (`mp3`, `m4a`, `ogg`, `wav`, `amr`), video (`mp4`, `3gpp`), `pdf`, vCard (`.vcf`), and iCalendar (`.ics`). Any other type is rejected with a `400`. Note that **HEIC** (the default iPhone photo format) and **SVG** are not supported — convert HEIC to JPEG/PNG before sending.

```bash title="CLI"
dial message \
  --to +14155550123 \
  --body "Here's your call summary" \
  --from-number-id pn_123 \
  --media ./summary.png \
  --media https://your-cdn.example.com/chart.jpg
```

```typescript title="Node"
const message = await dial.sendMessage({
  to: "+14155550123",
  fromNumberId: "pn_123",
  body: "Here's your call summary",
  media: [
    { path: "./summary.png" },                    // local file
    "https://your-cdn.example.com/chart.jpg",     // public URL
  ],
});
console.log(message.media[0].url);
```

```python title="Python"
from pathlib import Path

message = await dial.send_message(
    to="+14155550123",
    from_number_id="pn_123",
    body="Here's your call summary",
    media=[
        Path("./summary.png"),                    # local file
        "https://your-cdn.example.com/chart.jpg", # public URL
    ],
)
print(message.media[0].url)
```

```bash title="cURL (URLs)"
curl -X POST https://api.getdial.ai/v1/messages \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"to":"+14155550123","fromNumberId":"pn_123","body":"Here is your call summary","mediaUrls":["https://your-cdn.example.com/chart.jpg"]}'
```

```bash title="cURL (file upload)"
curl -X POST https://api.getdial.ai/v1/messages \
  -H "Authorization: Bearer sk_live_..." \
  -F to=+14155550123 \
  -F fromNumberId=pn_123 \
  -F body="Here's your call summary" \
  -F media=@summary.png
```

Dial sends over SMS. The `to` and `from` numbers must both be in E.164 format.

True MMS delivery is supported only for **US and Canada** numbers. To other destinations, the carrier delivers the message as an **SMS containing a link** to the media rather than an inline attachment (and returns an error if that conversion is disabled). The media itself is still hosted by Dial at the `media[].url` either way.

**iMessage numbers.** A message with media sent from an iMessage number is delivered as **native attachments** when the recipient supports rich messaging (iMessage or RCS) — text and media arrive together as one message. When the recipient supports neither (e.g. an Android device without RCS), Dial delivers the same message as a **text with the media links appended**, from the same number. The message's `media` array is identical either way.

Sending isn't idempotent — there's no idempotency key, so a retry sends a **second** SMS. If a request fails ambiguously, [confirm before re-sending](/documentation/reference/errors#retries-and-idempotency) rather than blind-retrying.

## Next

#### [Receive an SMS](/documentation/capabilities/receive-an-sms)

Wait for a reply or one-time code.

#### [Place a voice call](/documentation/capabilities/place-a-voice-call)

Make an AI voice call instead.