OpenAPI recipes cookbook
The sample-writing policy guarantees a two-language floor on every sample, and the language-policy page spells out which endpoints carry the full six-language group. Both ship single-call snippets. This cookbook is the next level: each recipe is a complete, runnable sequence — call, keep the id, call again — with the envelope rules written down once instead of discovered one 422 at a time. Every recipe runs in cURL, Node.js, and Python, per the two-language floor plus the Python policy. Use a test key (dv_test_sk_…) so each chain
runs against the sandbox; the only places that use a different key shape are
the public-key endpoints, and the recipe says so inline. Field and parameter
definitions live on the linked API-reference pages — this page is the
sequence, not the schema.
Task index
Authenticate every request with
X-API-Key. Base URL
https://api.orbit.devotel.io/api/v1 — the sandbox is the same URL with a
dv_test_sk_… key, not a separate host. (Recipe 3 opens with the public-key
endpoint under /sdk, which deliberately sits outside /api/v1 — see the
SDK API page.)
1. Send an SMS, then poll its status
The send returns the message id synchronously and the delivery state asynchronously. A webhook is the durable way to consume the status change (wire one up); until then, pollGET /messages/{id} until data.status leaves queued — the lifecycle
values are on Message status lifecycle.
message.delivered and message.failed; the signed-body verification loop
per language is in Verify webhook signatures.
2. Create a contact idempotently, then upsert custom fields
Two discipline points in one recipe:Idempotency-Key on the create so a
safe retry can’t duplicate the row, and custom-field values written through
the values endpoint so the write validates against the field definition.
At least one of phone / email is required on the create; field keys and
types are defined first under /api/v1/custom-fields.
custom_fields — re-read
GET /contacts/{id} to see the upsert. The full idempotency contract
(replay headers, TTL, 409 on conflicting bodies) is in
Idempotent requests.
3. Build a segment over CDP events
The event-ingest endpoint is public-key — it is safe to call from browser or mobile code. Segment building is secret-key — it is a back-office read over your whole contact base. This split is the entire reason the two calls take different keys:- Ingest with the tenant public key (
dv_test_pk_…in test mode) inX-API-Key, on the/sdk/trackpath outside/api/v1. - Segment with the secret key (
dv_test_sk_…) inX-API-Key, on/api/v1/segments/export.csvwith a filter body referencing the ingested event.
{ data, meta } envelope — the body is the
file. Event ingestion is versioned separately from the REST API
(SDK API); the export’s filter grammar is on the
Segments API. In a browser, use the
@devotel-orbit/web SDK rather than hand-rolling the identify
- track calls.
4. Upload a file and attach it to a send
Uploaded files return a file id and a signed URL; channel sends accept the media URL on the payload. This recipe uploads an image and rides the URL into an MMS. The 25 MB upload cap and the signed-URL TTL are on the Files API page.file_id flow drives WhatsApp template sends — pass the URL in the
template’s media component. Oversized or HEIC/TIFF source images need to be
re-encoded first with POST /messages/media/convert; the recipe surfaces
that branch inside the MMS page rather than blocking the upload.
5. Bootstrap a reseller subaccount
The five calls a CSPaaS reseller runs to take a child from row to priced-and-capped. Each is idempotent-safe to rerun with the same body — thePUTs are full-set replaces.
POST /subaccounts/provision
walks the create + pricing + funding + branding chain atomically, and the
dashboard subaccount wizard drives the same steps. The five discrete calls
stay canonical for resellers scripting the lifecycle from their own tooling.
Every endpoint here requires an owner or admin role on the parent
organization; scope and shape details per endpoint are on the
Subaccounts reference.
6. Read a subaccount’s live usage counters
Budgets are enforced send-side against a near-real-time counter; the statement settles the authoritative spend afterward.GET usage/live
bridges the two — the dashboard’s live-usage widget reads it, and your
tooling can poll it directly for a last-moment pre-warn.
spent_cents (reconciled statement spend),
live_counter_cents (the send-time gate counter the budget enforces
against, per the response shape the Subaccounts page documents), and
breached (true when the counter passes the channel budget’s
threshold_cents, when a budget is set). The route serves no-cache
responses — poll it; do not memoize.
7. Stream the live request log to a terminal
GET /logs/tail is a SSE stream, not a buffered fetch — the generic
request() helper never returns until the stream closes, so consume it
with raw HTTP and an incremental frame parser. The event catalog is
event: log rows; the API page for the endpoint is
Live request-log tail.
method, path_pattern,
status_code, and duration_ms — plenty to grep against a stuck
integration without shipping the whole request body anywhere.
8. The envelope rule every recipe inherits
Every response — success or failure — is the same shape, so your code can branch once instead of per-endpoint. Success (2xx). The payload is underdata; meta.request_id and
meta.timestamp always accompany it. List endpoints add
meta.pagination. Quote the request_id when you open a support issue —
it is keyed into the request log recipe 7 streams.
200 success
ValidationError; raw clients should read data.errors for the
per-field issues the schema rejected. The envelope still closes in meta.
422 validation failure
UNAUTHORIZED /
INVALID_API_KEY); scope failures 403 (INSUFFICIENT_PERMISSIONS);
throttles return 429 with a Retry-After header and a retry_after field
on the body. The canonical code table is on Error codes;
the error-handling examples walk the
retry decision tree per language. All six languages follow the same
envelope — the per-language unwrap idioms are on
How request samples work.
See also
- API Overview — base URL, auth, pagination, and the envelope this cookbook builds on
- REST API recipes — the task-by-task cookbook with all six languages on every flow
- Per-language recipes — the same four-way loop in Python / Go / Ruby / PHP / Java / C#
- Idempotent requests — the full Idempotency-Key contract recipe 2 leans on
- Error handling examples — typed error handling per language