Skip to main content

Send & Receive Messages

This guide takes you from an API key to a working two-way message flow: send an outbound message, track its delivery, and receive the reply on your own server. SMS is used as the running example — the same three-step pattern (send → track → receive) applies to WhatsApp, RCS, Viber, and Email, each on its own endpoint. You will:
  1. Send a message
  2. Track delivery
  3. Receive inbound messages
  4. Reply to an inbound message

Prerequisites

  • An API key from Settings → API Keys. Use a sandbox key (dv_test_sk_…) while you build — sandbox sends are free and simulated, and return deterministic delivery receipts so you can exercise both the success and failure paths. Swap in a live key (dv_live_sk_…) for production.
  • A sender number that can send on the channel you’re using. For SMS that’s an SMS-capable number you own (search and buy one via the Numbers API, or from Dashboard → Numbers). Omit from and Orbit picks an eligible sender for the destination.
  • A public HTTPS URL for the receive step. Any tunneling tool works while developing.
All requests go to a single base URL — sandbox is the same host, selected by your key, not a different domain:
Every request carries your key in the X-API-Key header.

1. Send a message

Send an SMS with POST /messages/sms. Only to and body are required; from is optional.
The response is 202 Accepted — the message is persisted and queued for delivery, not yet handed to the carrier. Fields live under data; meta carries the request_id you should log for support.
The id (msg_ followed by 32 hex characters) is the handle for every follow-up call — status lookups, delivery webhooks, and the message trace all key off it. Sandbox responses add "test_mode": true to meta.
Always send an Idempotency-Key on sends. Replaying the same key and body within 24 hours returns the original response instead of sending a duplicate; replaying it with a different body returns 409 IDEMPOTENCY_KEY_REUSED.

Other channels

Each channel has its own endpoint under the /messages prefix with a body shape suited to that channel. There is no single channel-polymorphic route — pick the endpoint that matches your channel:

2. Track delivery

A queued message moves through a status lifecycle before it reaches the recipient:
You have two ways to follow it: Poll the message by id:
Subscribe to webhooks (recommended — no polling). One event fires per status transition, each carrying the message id and new status:
  • message.sent — accepted by the carrier
  • message.delivered — confirmed delivered to the handset
  • message.failed — terminal failure (the payload’s status distinguishes failed, undelivered, expired, and submitted_no_receipt; error_code / error_message carry the provider reason when present)
Register a webhook endpoint once, then let events flow:
A delivery event looks like this:
Verify the X-Orbit-Signature header before trusting any payload, and deduplicate on the event id — delivery is at-least-once. See Webhooks security for the verification snippet.

3. Receive inbound messages

When someone replies to your number (or messages it first), Orbit records an inbound message and — if you subscribed to message.received in the step above — POSTs it to your webhook URL:
The inbound message is also queryable — list everything you’ve received with the direction=inbound filter:
Inbound routing is automatic for numbers you own on Orbit — a reply to any of your sender numbers is captured and, with a message.received subscription, delivered to your webhook. No per-number inbound URL wiring is required.

4. Reply to an inbound message

Replying is just another send, addressed back to the inbound from. Swap to and from and call POST /messages/sms again:
Both messages share a conversation, so you can pull the full back-and-forth with the conversation_id filter on GET /messages once the thread exists.

Common errors

Every error uses the same shape — match on error.code (an UPPERCASE constant), and log meta.request_id: Full list: Error Codes.

Next steps