saperly
Guides

Messaging

Send and list SMS from your Saperly numbers over the v2 API — consent and disclosures are enforced automatically.

Send and list SMS from your Saperly numbers. Consent and disclosures are enforced automatically on every send — see Compliance. Inbound messages are delivered to your webhook.

Endpoints

Base URL https://api.saperly.com. Authenticate with Authorization: Bearer sap_sk_live_...; the workspace is resolved from the token.

MethodPathReturnsScope
POST/messagesMessage (201)write
GET/messagesMessage[]read

GET /messages accepts an optional numberId query parameter to filter by sending number.

Send an SMS

POST /messages takes the following payload:

FieldTypeNotes
fromNumberIdstringThe Saperly number to send from.
tostringRecipient in E.164 format (e.g. +15555550123).
bodystring1–1600 characters.
import { configure, messaging } from '@trysaperly/sdk'

configure({ apiKey: process.env.SAPERLY_API_KEY! })

const { data: message, error } = await messaging.send({
  body: {
    fromNumberId: 'num_...',
    to: '+15555550123',
    body: 'Your code is 123456.',
  },
})

if (error) {
  // typed error — e.g. RecipientOptedOut, InsufficientFunds, MessageSendFailed
} else {
  console.log(message.id)
}
import os
import httpx

message = httpx.post(
    "https://api.saperly.com/messages",
    headers={"Authorization": f"Bearer {os.environ['SAPERLY_API_KEY']}"},
    json={
        "fromNumberId": "num_...",
        "to": "+15555550123",
        "body": "Your code is 123456.",
    },
).json()

print(message["id"])
curl -X POST https://api.saperly.com/messages \
  -H "Authorization: Bearer $SAPERLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fromNumberId": "num_...",
    "to": "+15555550123",
    "body": "Your code is 123456."
  }'

If the carrier rejects the send, the call fails with MessageSendFailed (502).

Outbound SMS requires consent

Sending to a peer requires recorded consent first. Saperly enforces this automatically and will block uncompliant sends — record explicit_outbound consent before messaging a number you initiated contact with. See Compliance.

List messages

const { data: messages } = await messaging.list({ query: { numberId: 'num_...' } })
messages = httpx.get(
    "https://api.saperly.com/messages",
    headers={"Authorization": f"Bearer {os.environ['SAPERLY_API_KEY']}"},
    params={"numberId": "num_..."},
).json()
curl "https://api.saperly.com/messages?numberId=num_..." \
  -H "Authorization: Bearer $SAPERLY_API_KEY"

Inbound, STOP, and HELP

Inbound SMS is delivered to your configured webhook. The STOP and HELP keywords are handled automatically:

  • STOP revokes the sender's consent — subsequent outbound sends to that number are blocked.
  • HELP replies with the standard help message.

Body length and segmentation

The body limit is 1600 characters. Longer messages are segmented by the carrier into multiple SMS parts and reassembled on the recipient's device; you are billed per segment.

On this page