Developers

Send from your own code.

A REST API to send, and signed webhooks to hear what happened next. Messages still leave through your own phones and accounts. Included with the Bundle plan.

message #1042 · illustrationlive
  1. POST /api/v1/messages/smsyour code
  2. sms.queuedwebhook
  3. sms.sentwebhook
  4. sms.deliveredwebhook
  5. sms.received“Can I change the pickup time?”

REST API

One request sends a message.

The API lives at https://app.smsegress.com/api/v1. Create a key in your dashboard and send it as a bearer token.

POST /api/v1/messages/sms
curl -X POST https://app.smsegress.com/api/v1/messages/sms \  -H "Authorization: Bearer $SMSEGRESS_API_KEY" \  -H "Idempotency-Key: order-1042-ready" \  -H "Content-Type: application/json" \  -d '{"to": "+447700900123",       "body": "Your order #1042 is ready for pickup."}'
  • Keys with scopes

    Each API key carries only the abilities you give it, such as sms.send, contacts.view or campaigns.manage.

  • Rate limits

    60 requests a minute for each key, and 180 a minute across a workspace.

  • Safe retries

    Send an Idempotency-Key header with a write, so a retry does not send twice.

Ten of the endpoints, all under /api/v1
POST /messages/smsSend an SMS through a phone or provider account
POST /messages/whatsappSend from a linked WhatsApp number
GET /messages/{id}Read one message and its status
POST /messages/{id}/cancelCancel a message that has not left yet
POST /campaignsCreate a campaign
POST /campaigns/{id}/launchLaunch it. Pause, resume and cancel work the same way
PUT /contactsCreate or update a contact
GET /unsubscribedList the contacts who opted out
GET /devicesList paired phones
GET /conversationsList inbox conversations

The full reference, a request tester and an OpenAPI file are in your dashboard under API docs.

Webhooks

Then hear what happened next.

Point a webhook at your endpoint and choose the events you want. Each delivery is signed, so you can check it came from SmsEgress before you trust it.

SMS
sms.queuedsms.sentsms.deliveredsms.failedsms.received
WhatsApp
wa.message.sentwa.message.receivedwaba.message.sentwaba.message.received
Contacts
contact.createdcontact.unsubscribed
Campaigns
campaign.startedcampaign.completedlink.clicked
Inbox
conversation.createdconversation.assignedconversation.resolved
Phones
device.onlinedevice.offline
  • Signed

    X-Webhook-Signature carries a timestamp and an HMAC‑SHA256 of the timestamp, a full stop and the raw body, keyed with that webhook’s secret.

  • Retried

    A delivery that fails is tried up to 7 times: after 1 minute, 5 minutes, 30 minutes, 3 hours, 24 hours and 72 hours.

  • Switched off when it is clearly broken

    Answer 410 Gone and the webhook is disabled. So is one that fails 20 deliveries in a row, and the owner is told.

verify.mjs · example
import { createHmac, timingSafeEqual } from 'node:crypto'; // header: X-Webhook-Signature: t=1731240000,v1=5f2c...export function verify(rawBody, header, secret) {  const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');  return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));}

Build it on numbers you own.