Skip to main content

Documentation Index

Fetch the complete documentation index at: https://orbit-docs.devotel.io/llms.txt

Use this file to discover all available pages before exploring further.

API Integration

Orbit is API-first. Every feature you can click in the dashboard is backed by a REST endpoint you can call from your own code. This page is the map to the rest of the API reference — where to start, what to test against, and what to watch for in production.

Base URLs

EnvironmentBase URLUse for
Productionhttps://orbit-api.devotel.io/apiLive traffic, billed
Sandboxhttps://sandbox-api.devotel.io/apiIntegration testing, free, test credits granted automatically
The sandbox mirrors production endpoints 1:1. Requests don’t move real money, don’t send real messages (SMS/WhatsApp/RCS/email/voice are captured, not dispatched), and don’t touch your production tenant. Authentication works the same way — you get a separate API key for sandbox in Dashboard → Settings → API Keys → Sandbox.
Sandbox responses include a x-orbit-env: sandbox header so your integration can assert it’s talking to the right environment.

Authentication

Every request needs an API key in the Authorization header:
curl https://orbit-api.devotel.io/api/messages \
  -H "Authorization: Bearer sk_live_abc123..." \
  -H "Content-Type: application/json"
  • Keys are per-tenant. Subaccounts get their own scoped keys.
  • Keys have prefix: sk_live_ (production) or sk_test_ (sandbox).
  • Rotate keys anytime at Settings → API Keys — rotation is instant, old keys invalidate.
  • Leaked a key? Revoke immediately; Orbit does not charge you for traffic after the revoke timestamp.
Full detail: Authentication.

Channels covered by the API

Everything in the dashboard is available via API. Same payloads, same behavior.
ChannelEndpoint rootReference
SMS (including 10DLC, short-code, toll-free)/messages with channel=smsMessaging API
WhatsApp Business/messages with channel=whatsappMessaging API
RCS Business Messaging/messages with channel=rcsMessaging API
Viber Business/messages with channel=viberMessaging API
Email (SMTP + transactional)/messages with channel=emailMessaging API
Voice (inbound, outbound, AI agent, IVR)/voice/callsVoice API
Verify (OTP codes, Silent Auth)/verifyVerify API
Numbers (search, purchase, release, port)/numbersNumbers API
Contacts & lists/contactsContacts API
Campaigns/campaignsCampaigns API
AI Agents/agentsAgents API
Flows/flowsFlows API
Billing & usage/billingBilling API
The /messages endpoint is channel-polymorphic: you specify channel, and the payload shape adapts. The same JSON shape gets you SMS delivery, WhatsApp template sends, RCS rich cards, and email — the only difference is what’s inside the content block.

Your first API call

The fastest way to confirm your key works:
curl https://orbit-api.devotel.io/api/messages \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "sms",
    "from": "+16572262362",
    "to": "+15555551234",
    "content": { "text": "Hello from Orbit" }
  }'
Response:
{
  "id": "msg_01HXYZ...",
  "status": "queued",
  "channel": "sms",
  "cost_estimate": { "currency": "USD", "amount": 0.0075 },
  "created_at": "2026-04-17T10:23:00Z"
}
Check status at GET /messages/:id or subscribe to delivery webhooks (recommended).

Webhooks

Webhooks are how Orbit tells your backend about events: a message was delivered, a call connected, an agent escalated a conversation, a number was ported in. You register a URL and we POST events to it. Setup
  1. Dashboard → Settings → Webhooks → Add endpoint
  2. Enter your HTTPS URL (HTTP rejected in production)
  3. Pick the event types you want (or subscribe to * for everything)
  4. Copy the signing secret (whsec_...) — you need it to verify signatures
Every Orbit webhook carries an Orbit-Signature header. Verify it before trusting the payload.
// Node example
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(body: string, header: string, secret: string) {
  const [t, v1] = header.split(",").map(p => p.split("=")[1]);
  const expected = createHmac("sha256", secret)
    .update(`${t}.${body}`)
    .digest("hex");
  return timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}
Replay protection: reject any signature whose t (timestamp) is older than 5 minutes. Retries Orbit retries failed webhooks with exponential backoff: 1s → 5s → 25s → 2m → 10m → 1h → 6h. After 6h we stop retrying and flag the endpoint. You can replay from Dashboard → Webhooks → Event log. A webhook is “successful” if your server returns any 2xx within 10 seconds. Anything else (timeout, 5xx, 4xx except 410 Gone) triggers retry. Return 410 Gone to permanently disable a single event (e.g., you’ve deprecated handling for that type). Dead endpoints If we see 20 consecutive failures, we auto-disable the endpoint and email the org admin. Re-enable it from the dashboard once you’ve fixed it. Full detail: Webhooks overview, Event catalog, Security.

Rate limits

Default limits apply per API key. Higher limits available on request.
Endpoint familyDefault rateNotes
/messages (send)600 / min across all channelsPer-key. Carriers / Meta / Apple have their own throttling on top.
/messages (read)3,000 / min
/voice/calls (outbound)60 / minPer-number concurrency limits also apply from your provider.
/contacts, /campaigns, /agents, /flows300 / min
/numbers/search120 / min
/billing120 / min
Auth / signup / password reset20 / min per IP
Rate-limit headers are on every response:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 583
X-RateLimit-Reset: 1713357600
When you exceed the limit you get HTTP 429 with:
{ "error": { "code": "rate_limited", "retry_after": 12 } }
Use retry_after (seconds), not fixed backoff. Full detail: Rate Limits.

Error codes

All errors follow the same shape:
{
  "error": {
    "code": "insufficient_balance",
    "message": "Your wallet balance of $0.12 is below the $0.50 minimum to send this WhatsApp conversation. Add funds or lower send volume.",
    "request_id": "req_01HXYZ...",
    "documentation_url": "https://docs.devotel.io/reference/error-codes#insufficient_balance"
  }
}
Always log request_id — support can trace it end-to-end. Common codes you’ll hit during integration:
CodeHTTPMeaningFix
invalid_api_key401Key revoked, wrong env, or typoCheck Settings → API Keys
tenant_suspended403Usually billing or complianceCheck dashboard banner
rate_limited429Too many requestsRespect retry_after
insufficient_balance402Wallet empty or below channel minTop up wallet
invalid_number400Not E.164 or not reachableValidate upstream
number_not_sms_capable400Toll-free without 10DLC reg, or inbound-only numberRegister or swap number
waba_template_not_approved400Template still pendingCheck status in /templates
channel_not_enabled403Tenant hasn’t finished channel onboardingGo to Settings → Channels
duplicate_idempotency_key409Same Idempotency-Key reused with different bodyUse unique keys per send
validation_error400Payload shape wrong — error.fields lists each issueRead error.fields
Full list: Error Codes.

Idempotency

Every POST that creates a resource (message, call, contact, campaign) accepts an Idempotency-Key header. Use it.
curl https://orbit-api.devotel.io/api/messages \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: order-conf-98421" \
  -H "Content-Type: application/json" \
  -d '{...}'
Orbit stores the response for 24 hours. Replay the exact same key + body → you get the cached response, not a duplicate send. Replay with a different body → 409 duplicate_idempotency_key. This is the single most important pattern for production reliability. Use it on every creation endpoint.

Pagination

List endpoints return cursor-paginated results:
{
  "data": [...],
  "has_more": true,
  "next_cursor": "eyJpZCI6Im1zZ18wMUhYWVovLi4uIn0="
}
Pass ?cursor=<next_cursor>&limit=100 for the next page. No offset — cursor is stable against concurrent inserts. Full detail: Pagination.

Postman collections

Pre-built collections per channel — import one and you’re testing in 30 seconds.
CollectionImport link
Orbit — Core (auth, messages, contacts, webhooks)postman/orbit-core.json
Orbit — Voice & IVRpostman/orbit-voice.json
Orbit — Campaigns & Flowspostman/orbit-campaigns.json
Orbit — Agents (AI)postman/orbit-agents.json
Orbit — Numbers & Verifypostman/orbit-numbers-verify.json
Orbit — Billing & Usagepostman/orbit-billing.json
Each collection has variables for {{base_url}} and {{api_key}} pre-wired. Default environment is sandbox. Also available: OpenAPI 3.1 spec at https://orbit-api.devotel.io/api/openapi.json — import into any codegen (openapi-generator, oats, orval, kiota).

SDKs

If you’d rather not hand-roll HTTP, we maintain official SDKs. They handle auth, retries, pagination, webhook signature verification, and typed models. Browser: use the Orbit JS SDK for client-side widgets (chat embed, voice browser calling). Never ship a server key to the browser — use a short-lived session token from /auth/browser-token.

Environments & promotion

Recommended integration flow:
  1. Develop against sandbox with sk_test_* keys
  2. Stage against sandbox with your staging webhook URL (not production)
  3. Prod swap keys + webhook URL; everything else identical
Sandbox retains data for 30 days then purges. Do not rely on it for long-term storage.

Support

Always include request_id from the failing response in any support ticket — it’s our fastest route to root cause.