---
title: "Send an SMS through one of the caller's own eSIMs"
method: POST
path: "/numbers/messages"
tags: ["Messages"]
---

# Send an SMS through one of the caller's own eSIMs

`POST /numbers/messages`

Sends an outbound SMS through one of the caller's own eSIMs, addressed by `esimId`. Returns 404 if the eSIM doesn't exist or isn't owned by the caller. Returns 409 when the eSIM isn't in a state that can send (see `reason`). May be disabled for this deployment (403 `send_disabled`), and is subject to a per-owner daily cap and a burst rate limit (429).

Idempotent via `clientRequestId`: replaying the same key with the same payload returns the original send; the same key with a different payload returns 409 `idempotency_conflict`. The message is queued and sent asynchronously — this call returns 202 immediately.

## Request body

- SendMessageInput
  - `esimId` string, uuid, required — The eSIM this send goes out through — resolves the device/subscription; a foreign or nonexistent id is a 404
  - `to` string, required — Recipient phone number — normalized to E.164 (spaces/dashes/dots stripped); rejected with 400 if it doesn't validate as E.164 afterward.
  - `body` string, required — SMS body text (max 320 chars — smaller than the admin tier's cap; see the schema's own doc comment for why)
  - `clientRequestId` string — Client-supplied idempotency key, scoped to (owner, esimId, key). Replaying the same key + identical payload returns the original send; the same key with a DIFFERENT payload is a 409 conflict.
  - `deliveryReport` boolean — Wait for physedge to confirm carrier delivery before completing the send (adds executor-side latency, never on this request — sends are always async/202). Defaults to false for the public tier (opt-in, unlike the admin tier's default-true).

## Response `202`

Outbound message queued

- object
  - `data` PublicMessageRow, required
    - `id` string, uuid, required
    - `esimId` string, uuid, nullable, required
    - `direction` 'inbound' | 'outbound', required
    - `status` 'received' | 'queued' | 'claimed' | 'sending' | 'sent' | 'sent_unconfirmed' | 'delivered' | 'failed', required
    - `peerNumber` string, nullable, required
    - `peerKey` string, nullable, required
    - `contactName` string, nullable, required
    - `body` string, nullable, required
    - `deliveryStatus` string, nullable, required
    - `detectedSender` string, nullable, required
    - `providerCode` string, nullable, required
    - `occurredAt` string, date-time, required
    - `createdAt` string, date-time, required

## Other responses

- `400` — Bad Request
- `401` — Unauthorized
- `403` — Forbidden — public SMS send is disabled for this deployment (see `reason`)
- `404` — Not Found
- `409` — Conflict — see `reason` for the stable, machine-readable cause
- `429` — Too many requests — daily cap or burst rate limit exceeded (see `reason`)
- `500` — Internal Server Error
- `503` — Service Unavailable — the relevant product is disabled for this deployment (see `reason`/`message`)

## Changes

- **2026-08-13** `02da4f9ce3fd` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/droidrun/apis/droidrun-cloud/changes/numbers/messages/post.md)

---

[API](https://skmtc.dev/droidrun/apis/droidrun-cloud.md) · [All operations](https://skmtc.dev/droidrun/apis/droidrun-cloud/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/droidrun/droidrun-cloud/revisions/02da4f9ce3fd/schema)
