REST API recipes
This is the cookbook the SDK index points at: since no SDK is published to a package registry yet, every recipe here is REST-first with the Node SDK (source-only, unpublished) as the second snippet. Ten tasks cover the integrations most teams build first — loyalty, OAuth integrations, and internal team chat round out the messaging, verification, contacts, and risk loops below. Each one is self-contained — pick the task you need, run it against the sandbox with adv_test_sk_… key, then swap in your live key.
Authenticate every request with X-API-Key. Base URL is https://api.orbit.devotel.io/api/v1 — sandbox is the same URL with a test key, not a separate host. Full primer: API integration.
Task index
1. Send a message, poll status, receive the webhook
Send one SMS, learn its id, then track delivery two ways: a direct status poll and a delivery webhook. Webhooks are the production answer — poll once to confirm the send works, then move to events. Send:cURL
Node SDK
202 Accepted returns the persisted send in the standard envelope. data.id (msg_…) is your handle; data.status starts as sent (or test_sent in sandbox) and moves as the carrier reports.
Poll status once:
cURL
Node SDK
message.delivered and message.failed (or * for everything). Orbit POSTs each status change to you; verify the X-Orbit-Signature header before trusting the body — verification is one function call per API integration → Webhooks. Polling is for the first integration smoke test; production consumers subscribe.
Canonical pages: Messaging API, Webhook events.
2. Handle a 429 with retry_after
When your send rate passes the per-key limit, the API answers 429 with a body that names the wait inside the envelope (error.retry_after) and again as a Retry-After header. Read whichever your HTTP client exposes; honor it before any fallback backoff — doubling on your own can retry inside the window and re-429.
cURL
Node SDK
3. Verify an OTP, end to end
Two requests: send the code, then check what the user typed. The platform generates, delivers, and expires the code; your backend never stores or compares it. Send — capturedata.verification_id from the response:
cURL
Node SDK
cURL
Node SDK
status: "approved" is the only pass. failed means the code didn’t match; re-check with the same verification_id until attempts_remaining hits 0, then send a fresh one. expired means the send’s TTL lapsed — send again. In sandbox, a test key simulates delivery; find the expected code in the verification’s dashboard row.
Canonical page: Verify API. A longer REST-only walkthrough (the same two calls plus webhook fallback): Verify integration without our SDK.
4. List and cursor-paginate
List endpoints return stable cursors — no offsets, so concurrent inserts can’t shift your page. Request the first page without a cursor, then passmeta.pagination.cursor until meta.pagination.has_more is false.
cURL
Node SDK
INVALID_CURSOR — restart the iteration without a cursor; never reuse cursors across requests changed in filters or across retries older than the same-minute window. Full table of cursor-vs-offset endpoints: Pagination.
5. Batch-import contacts, poll the import job
For anything past a few hundred rows, skip per-rowPOST /contacts calls and enqueue one async import. The endpoint accepts the parsed CSV as a rows array, returns 202 with a job_id, and runs the insert in the background. Poll the job id for progress.
cURL
Node SDK
status is no longer pending/running:
cURL
Node SDK
merge_strategy: "skip" (the default) leaves existing contacts untouched on duplicate keys; "merge" updates them with your new values. A completed row besides the failed ones reports per-row outcomes; rows that failed validation are listed as skipped, not imported as empty records. A 503 IMPORT_QUEUE_UNAVAILABLE means the background queue is briefly down — retry after a few seconds; don’t fall back to hundreds of single creates under load.
Canonical pages: Contacts API, Import contacts guide.
6. Score a destination before you send
POST /risk/score fuses the platform’s fraud detectors into one 0–100 verdict you can query before committing an SMS send or verify. It’s read-only and advisory — it never dispatches, so it’s safe to burn before every high-cost send.
cURL
Node SDK
recommendation: allow — proceed to the send; review — queue for a human only if the send is high-value; block — suppress the send and log the decision. band is the same verdict bucketed; score (0–100) is the worst-signal composite — the per-channel breakdown names which detector fired. This is a gate on your send decision, not a platform block: enforcement stays in your code, and outbound SMS still exits only through the platform.
Canonical page: Risk API. The pumping-attack context this protects against: SMS pumping protection.
7. Run a loyalty earn → preview → redeem round trip
Points come from events you already send to the CDP — loyalty computes a member’s balance from your event history when you check it, so there is no separate loyalty ledger to sync. The round trip: dry-run the program with preview, read a member’s balance, then burn points. Preview — evaluate your active program (or an override in the request body) against sample events. Nothing writes to the database, so burn this freely while you tune earn rules:cURL
Node SDK
order-completed and loyalty.points_redeemed in the ledger: loyalty.points_adjusted (an operator’s manual credit or debit) and loyalty.program_configured (a revision under a config sentinel contact that members never write):
cURL
Node SDK
balance is the spendable remainder; use GET /loyalty/members (paginated) to list every member or a specific id for one. The traits block also exposes loyalty_points_balance, loyalty_tier, and friends exactly as segments and journeys see them.
Redeem — burn points atomically. Concurrent redemptions against the same contact serialize, so a double-click can’t overspend:
cURL
Node SDK
201 Created with the redemption event id and the post-burn balance. An overspend returns 409 INSUFFICIENT_POINTS with the available remainder — burden the retry on a smaller amount or abort. Manual operator adjustments (POST /loyalty/members/:contactId/adjust) share the same overspend-safe path.
Canonical page: Loyalty API. The program-design walkthrough this loop plugs into: Loyalty program setup.
8. Open an OAuth connection and pull synced records
The integrations loop is connect → status → data: start the OAuth flow, confirm the connection formed, then read the synced records. Connect and sync trigger are owner/admin-gated; status and data are reads any scoped key can run. Start the connect flow — get theauth_url you redirect the operator to:
cURL
Node SDK
404; an integration server misconfigured answers 503. Either is terminal — fix in Settings → Integrations, not in your retry loop.
Confirm status — the OAuth popup closing is not proof the connection formed; ask before you read data:
cURL
Node SDK
connected: false resolves cleanly (with an empty sync list) rather than an error, so branch on it without a try/catch. Once connected, the same payload carries the connection’s metadata and per-sync status.
Pull synced records — name the model (contacts, deals, …) in the query:
cURL
Node SDK
POST /integrations/:id/sync { "sync_name": "contacts" } instead of waiting for the scheduled cadence; a 502 on data reads means the upstream fetch failed, retry after a beat.
Canonical page: Integrations API. The full CRM loop (webhooks, writeback, debugging): Connect HubSpot & Salesforce end to end.
9. Coordinate the team in a team-chat channel
Team chat is the internal surface — channels, DMs, and huddles for the operators running your workspace, membership-gated so nothing ever reaches a customer. Useful when a back-office bot or dashboard posts handoff notes: list your channels, post to one, read it back. List your channels — every read is membership-scoped, so you see only channels your API key’s principal belongs to:cURL
Node SDK
cURL
Node SDK
403 here is a membership gate, not a key problem — the same principal must be on the channel from the same identity; have the owner add you with POST /team-chat/channels/:id/members. Reactions, threads, DMs, and huddles follow the same shape off the message id.
Canonical page: TeamChat API. The operator workflows behind it: Team Chat guide.
10. Errors as recipes
Errors are recipes too — the failure classes in API error handling by example map one-to-one onto the tasks above. Branch onerror.code, never on message text.
Anything not in this table: read
meta.docs_url in the error envelope — it links to the code’s remedy in the Error Code Reference. Branch on the retriable-vs-terminal table in API error handling by example.
See also
- API integration — base URLs, sandbox, rate limits, idempotency
- Error handling by example — the error envelope and retry classes
- Pagination — cursor vs offset semantics
- Starter examples — runnable repos these recipes appear in
- Loyalty program setup — earn rules, tiers, and the redemption flow behind Task 7
- Connect HubSpot & Salesforce end to end — the full CRM loop behind Task 8
- Team Chat — operator workflows behind Task 9
- SD runs, no registry yet — when the SDKs publish, every curl here translates one-to-one