<!-- VanceTel docs · /docs/sms -->

# Send an SMS

A single `POST /messages` sends an SMS from one of your numbers to any recipient. The call returns a [message object](#the-message-object) you can store and reconcile against later delivery events. Every message you send also lands in the [softphone inbox](#delivery-status) so your team sees the same conversation in real time.

  The messaging API is **write- and event-oriented**: you send with `POST /messages`, then react to
  inbound texts and delivery updates over [Webhooks](/developers/docs/webhooks). To look up a single message
  after the fact, use `GET /messages/{id}`.

## Send a message

Provide a `from` (an [E.164](/developers/glossary/e164) number you own), a `to` recipient, and a `body`. The API responds `201` with the created message in `queued` status.

**curl**

```bash
curl https://api.netexem.com/v1/messages \
  -H "Authorization: Bearer $NETEXEM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+15551234567",
    "to": "+15557654321",
    "body": "Your appointment is confirmed for 2pm — reply STOP to opt out.",
    "senderName": "Appointment Reminder Bot"
  }'
```

**Node**

```ts
const res = await fetch("https://api.netexem.com/v1/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.NETEXEM_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "+15551234567",
    to: "+15557654321",
    body: "Your appointment is confirmed for 2pm — reply STOP to opt out.",
    senderName: "Appointment Reminder Bot",
  }),
});
const message = await res.json();
```

**Python**

```python
import os, requests

res = requests.post(
    "https://api.netexem.com/v1/messages",
    headers={"Authorization": f"Bearer {os.environ['NETEXEM_API_KEY']}"},
    json={
        "from": "+15551234567",
        "to": "+15557654321",
        "body": "Your appointment is confirmed for 2pm — reply STOP to opt out.",
        "senderName": "Appointment Reminder Bot",
    },
)
message = res.json()
```

  Numbers use [E.164 format](/developers/glossary/e164) — a leading `+` and country code, e.g. `+15551234567`.
  Opt-out is handled automatically: recipients who have texted `STOP` are suppressed before delivery.
  See [Opt-out](/developers/docs/opt-out).

### Request fields

| Field       | Type       | Required | Description                                            |
| ----------- | ---------- | -------- | ------------------------------------------------------ |
| `from`      | `string`   | yes      | An [E.164](/developers/glossary/e164) number you own.             |
| `to`        | `string`   | yes      | Recipient in E.164 format.                             |
| `body`      | `string`   | yes      | Message text.                                          |
| `senderName` | `string` | no       | Name identifying the message source in the softphone inbox — a team member's name or an automation/script name. See [Sender identification](#sender-identification). |
| `mediaUrls` | `string[]` | no       | Media URLs to attach — sends as [MMS](#mms).           |

## The message object

A successful send returns the created message. The same shape is returned by `GET /messages/{id}`.

```json
{
  "id": "msg_01h…",
  "from": "+15551234567",
  "to": "+15557654321",
  "body": "Your appointment is confirmed for 2pm — reply STOP to opt out.",
  "direction": "outbound",
  "status": "queued",
  "createdAt": "2026-05-31T14:02:09Z"
}
```

| Field       | Type     | Description                                                                       |
| ----------- | -------- | --------------------------------------------------------------------------------- |
| `id`        | `string` | Unique message identifier, e.g. `msg_01h…`. Use it to look the message up later.  |
| `from`      | `string` | The sending E.164 number.                                                         |
| `to`        | `string` | The recipient E.164 number.                                                       |
| `body`      | `string` | The message text.                                                                 |
| `direction` | `string` | `outbound` for messages you send, `inbound` for messages you receive.             |
| `status`    | `string` | Lifecycle state — one of `queued`, `sent`, `delivered`, `failed`.                 |
| `createdAt` | `string` | ISO 8601 timestamp of when the message was created.                               |

## MMS

To send an MMS, include a `mediaUrls` array alongside `body`. Each entry is a publicly reachable URL to the media you want to attach; `body` is optional when media is present.

**curl**

```bash
curl https://api.netexem.com/v1/messages \
  -H "Authorization: Bearer $NETEXEM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+15551234567",
    "to": "+15557654321",
    "body": "Here is your receipt.",
    "mediaUrls": ["https://files.example.com/receipts/inv-1042.pdf"]
  }'
```

**Node**

```ts
const res = await fetch("https://api.netexem.com/v1/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.NETEXEM_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "+15551234567",
    to: "+15557654321",
    body: "Here is your receipt.",
    mediaUrls: ["https://files.example.com/receipts/inv-1042.pdf"],
  }),
});
const message = await res.json();
```

**Python**

```python
import os, requests

res = requests.post(
    "https://api.netexem.com/v1/messages",
    headers={"Authorization": f"Bearer {os.environ['NETEXEM_API_KEY']}"},
    json={
        "from": "+15551234567",
        "to": "+15557654321",
        "body": "Here is your receipt.",
        "mediaUrls": ["https://files.example.com/receipts/inv-1042.pdf"],
    },
)
message = res.json()
```

## Sender identification

The optional `senderName` field identifies who or what sent the message, displayed in the softphone inbox. It supports two primary use cases:

### For manual sends — team member names

When a team member sends a message, populate `senderName` with their name:

```json
{
  "from": "+15551234567",
  "to": "+15557654321",
  "body": "Hi! Just checking in on your order.",
  "senderName": "Sarah Chen"
}
```

The message appears in the softphone inbox labeled with Sarah's name, so your team can trace it back to a specific person.

### For automated sends — script and workflow names

When an automation, script, or workflow sends a message, use a descriptive name that identifies the automation:

```json
{
  "from": "+15551234567",
  "to": "+15557654321",
  "body": "Your appointment reminder: Tomorrow at 2:00 PM",
  "senderName": "Appointment Reminder Bot"
}
```

Other examples of automation names:

- `"Lead Follow-Up Sequence"` — for CRM drip campaigns
- `"Post-Sale Survey"` — for automated customer feedback
- `"Re-Engagement Campaign"` — for win-back workflows
- `"Notification System"` — for alert automations

### Why this matters

When multiple automations are running in parallel (appointment reminders, follow-up sequences, drip campaigns), the `senderName` becomes the primary way your team identifies which workflow or script triggered each message. Without clear sender identification, it's difficult to:

- **Audit** which automation sent a given message to a customer
- **Troubleshoot** unexpected messaging behavior
- **Report** on engagement by automation or workflow

If `senderName` is omitted, the message is attributed to the `from` number alone.

## Delivery status

The `status` on the returned object is its state at the moment of the request — almost always `queued`. Delivery is asynchronous, so the status advances after the response: `queued` → `sent` → `delivered`, or `failed` if the carrier rejects it.

Track those transitions with **delivery-receipt webhooks**. Subscribe an endpoint and you'll receive a signed event each time a message advances, so you can reconcile state without polling. Every event is HMAC-SHA256 signed in the `X-Netexem-Signature` header — verify it before trusting the payload. See [Webhooks](/developers/docs/webhooks) for the event shape and signature verification.

  Every message you send also appears in the **softphone inbox** in real time — your team sees the
  same conversation thread your code does, with no extra setup.

If you need to read a single message's current state directly, fetch it:

```bash
curl https://api.netexem.com/v1/messages/msg_01h… \
  -H "Authorization: Bearer $NETEXEM_API_KEY"
```

## Compliance

- **Opt-out is automatic.** Recipients who reply `STOP` are suppressed for the relevant number, and further sends to them are blocked before they reach the carrier. Honor and surface these states rather than working around them. See [Opt-out](/developers/docs/opt-out).
- **Use E.164 everywhere.** Both `from` and `to` must be [E.164](/developers/glossary/e164); malformed numbers return `422`.
- **Send from numbers you own.** `from` must be a provisioned number on your account.

## Next steps

- [Webhooks](/developers/docs/webhooks) — delivery receipts and inbound `message.received` events
- [Opt-out](/developers/docs/opt-out) — how STOP/START is handled for you
- [API reference](/developers/docs/api-reference) — try `POST /messages` live
