Call any route from every SDK
Every Orbit API route is reachable from every SDK, including routes the SDK’s typed modules do not wrap yet. Each client ships one untyped request method that carries the same authentication, the same automatic429/5xx retry with backoff, and the same typed errors as the typed
helpers. This page shows that call side by side with cURL and walks one
real flow end to end: build a segment, create a contact that matches it,
launch a campaign against it, and send.
The Sandbox API cookbook is the recipe
sheet for sandbox routes; this page is the equivalent for the escape
hatch itself, on any route.
1. Why the escape hatch exists
Typed coverage is core scope. The typed helpers each SDK ships wrap the routes most integrations touch, and the SDK packages are the coverage source of truth: this page promises nothing the packages do not ship. Routes go live as public API endpoints first, so a route below the typed modules is still a full citizen of the API: sameX-API-Key auth, same
rate limits, same { "data", "meta" } envelope, same error codes.
The untyped request method is how you reach those routes without
dropping to raw HTTP. Compared with a hand-rolled HTTP call it gives you:
- The same auth — the client attaches your
X-API-KeyandUser-Agent; you never assemble the header by hand. - The same retry —
429and5xxresponses are retried with exponential backoff that honoursRetry-After, and every mutating request carries anIdempotency-Key, so a retriedPOSTis not a double send. - The same typed errors — a
429that exhausts the retry budget surfaces as the SDK’s rate-limit error type (and a400/422as the validation error type), not a raw status code you have to switch on.
2. The unified call matrix
One row per language, same call. The one difference to memorise is the path base: cURL and the Node client take the full path including/api/v1; every other client takes paths relative to /api/v1.
Clients for the worked flow, one per language:
3. Worked flow: segment to campaign
The flow: create a segment from a filter, create a contact the filter matches (segment membership is materialised from the filter, so adding a contact to a segment means creating or updating a contact whose attributes match), create a campaign targeted at the segment, and send it. Four calls, shown in every language.Step 1 — Create the segment
POST /contacts/segments returns 201 with the created,
already-materialised segment.
Step 2 — Create a contact the segment matches
POST /contacts with attributes that satisfy the filter. The segment’s
auto_refresh flag keeps membership current; a manual
POST /contacts/segments/{id}/refresh re-materialises immediately.
Step 3 — Create the campaign on the segment
POST /campaigns with audience_type: "segment" and the segment id.
The campaign is created dormant; nothing sends until step 4.
Step 4 — Send it
POST /campaigns/{id}/send takes no body. Pre-launch gates (suppression
lists, frequency caps, quiet hours) run on this call exactly as they do
for a dashboard send.
The same flow in Java and C# (pre-publish)
Both pre-publish clients expose the identical escape hatch; vendor them from source per the Java and C# pages. The four steps collapse to the same three shapes: aPOST with a body, a
POST that reads an id back, and a bodyless POST.
4. The envelope: unwrap data, keep meta.request_id
Every response, raw or through an SDK, is the same envelope:
data for the resource, and log
meta.request_id next to the call in your own logs: when you open a
support ticket about a specific call, the request id is what ties your
trace to the server side. List reads additionally carry
meta.pagination with the next_cursor to pass back as ?cursor=.
5. Errors: the typed errors follow you down here
The escape hatch raises the same error types the typed helpers raise. A429 is retried up to the client’s retry budget; when the budget runs
out you get the language’s rate-limit type, and every error carries the
server’s error.code plus meta.request_id:
Honoring a 429 without an SDK
A raw caller owns its own retry. HonourRetry-After, cap the attempts,
and send a stable Idempotency-Key on mutating calls so the retry cannot
double-send:
6. Sandbox keys vs. X-Test-Mode
The escape hatch behaves identically in sandbox: point ORBIT_API_KEY
at a sandbox secret key and every sample on this page runs against your
sandbox org with nothing delivered and no live recipient touched.
Two distinct signals, not interchangeable:
- A sandbox key (
dv_test_sk_…) — the normal one for scripts and CI. The key prefix itself scopes the session to sandbox; the same request shapes work unchanged. - The
X-Test-Mode: trueheader — a dashboard-session signal. The console’s Test/Live toggle sets it for you; it is honoured only on dashboard sessions, and a live key (dv_live_sk_…) cannot flip into sandbox with it.
429 against a live key is a harness that retries against production
data. The sandbox overview walks all three
activation signals; the Sandbox API cookbook
pairs every sandbox route with its escape-hatch line.
7. Where to go next
- Sandbox API: worked examples — this page’s sibling for sandbox routes
- SDK sample coverage policy — which languages appear on which pages, and why
- SDK pages: Node.js · Python · Go · Ruby · PHP · Java · C# — each walks its escape hatch end to end