The SDK surface model
Orbit ships twelve language SDKs, and they are not twelve parallel attempts at the same thing. They share one architectural model: a fixed split into three tiers, one request contract every client honors, a shared error hierarchy, and a deliberate split between what a secret key can do and what a publishable key can do. This page explains that model. For installation and a first call in a specific language, go straight to the SDK index and the per-language pages linked at the end.The three tiers
Every SDK occupies exactly one tier, chosen by where it will run and what credential it can hold:
The split is a design decision, not a backlog. A client-scope SDK that
exposed
campaigns or billing would be embedding a server credential in
an installable binary; a core-scope SDK wrapping every niche sub-resource
would multiply the maintenance surface without adding capability, because
the escape hatch (below) already covers those routes.
The shared contract every SDK saturates
Whether the client is hand-written (Node) or codegen-wrapped (Python, Go, Ruby, PHP, Java, .NET), these behaviors are identical:- Auth header: every request carries
X-API-Key: <key>. Server SDKs expect a secret key; client-scope SDKs use the publishable key and call only public, key-tolerant surfaces. - Retry policy: non-2xx responses classified as retryable — a
429or any5xx— are retried up to 3 attempts per request by default, with exponential backoff starting at 250 ms and doubling each round, honoring the server’sRetry-Afterheader and clamped at a 60-second ceiling.4xxresponses other than429are surfaced immediately. - Idempotency: every mutating request (
POST,PUT,PATCH,DELETE) carries anIdempotency-Keyheader. The SDK generates a random UUID once per logical call and reuses it across the retry chain, so a retried send collapses to one charge on the server. You can pin your own key — in Node, passidempotencyon the send input. - Webhook verification: server-side SDKs expose a one-call verifier
over the
Orbit-Signatureheader (t=<unix_ts>,v1=<hex_hmac>, HMAC-SHA256 with your webhook secret, 5-minute replay window).
Webhook verification is server-side only. Client-scope SDKs never
receive inbound webhooks, so none of them ships a verifier.
How the codegen pipeline maps OpenAPI onto typed clients
The single source of truth isapps/api/openapi.yaml, and the pipeline at
tools/sdk-codegen turns it into the six non-Node server SDKs (Python,
Go, Ruby, PHP, Java, .NET). The shape we ship is two layers:
- Generated layer:
packages/sdk-<lang>/generated/holds raw openapi-generator output — verbose, auto-named, wiped and re-emitted from the spec on everypnpm sdk:codegenrun. Customers never import from it. - Handcrafted wrapper:
packages/sdk-<lang>/src/(orlib/) holds theOrbitClientconstructor, the auth helper, the error hierarchy, and the typed resource methods customers actually call. The wrapper imports types and low-level operations from the generated layer.
operationId becomes an idiomatic method.
Regeneration is idempotent, and a spec-drift gate
(pnpm sdk:check-spec-drift <lang>) fails a release when the published
spec’s hash no longer matches the language’s pinned hash — so a route
rename can’t slip through as a customer 404.
When the escape hatch is the right tool
Each core-scope client also exposes a low-level, untyped request helper —client.request in Node/Python/Java/Ruby, client.Request* in Go/.NET —
that applies the full contract (auth header, retries, idempotency, error
parsing) to any path. Use it for exactly two cases:
- a covered route the typed resources don’t wrap yet (RCS, agents, billing, voice IVR/dialer): the helper keeps the contract without pretending the route has a typed shape;
- a versioned preview or admin route you don’t want to hand-build headers for.
fetch.
Worked example: POST /messages/sms
The same endpoint at each layer, showing what the codegen-to-wrapper
mapping produces end to end.
1. The OpenAPI operation (apps/api/openapi.yaml):
operationId ends
as an idiomatic wrapper method:
OrbitApiError
(below) carrying statusCode, code, requestId, and on 429 the
Retry-After hint in details.
Publishable key vs. secret key
Orbit issues two key envelopes, and which one a client-scope SDK is allowed to hold is the whole point of the envelope split:- Secret key (
dv_live_sk_…) — full API access. Server-side only: it must never ship inside a mobile app, a browser bundle, a public repository, or a client-side environment variable. Every server SDK (GA Node, core-scope languages) authenticates with this. - Publishable key (
dv_live_pk_…) — public ingest + client-scope surfaces only: identified/anonymous event ingest (/sdk/identify,/sdk/track), conversations/inbox chat, in-app content cards, video rooms, and the voice-agent widget. Safe to embed in a distributed binary.
messaging, whatsapp, email, contacts,
campaigns, verify, billing, and every admin route — because those
require the secret key. If you need them from a backend, use the Node
SDK or call the REST API directly. A leaked publishable key
exposes only anonymous ingest, which is rate-limited per-key; a leaked
secret key exposes your whole tenant, so the envelope split exists.
The error hierarchy
Every SDK exposes the same tree, so acatch pattern you write in Node
ports to Python unchanged:
OrbitApiError(base) — catches any non-2xx API response. Carriescode,statusCode(aliased asstatus),details,docsUrl, andrequestId, plusisRateLimited/isClientError/isServerErrorgetters.OrbitAuthError— 401/403: bad key or missing scope.OrbitNotFoundError— 404: resource absent or not visible.OrbitRateLimitError— 429: retryable; checkRetry-Afterindetailsbefore retrying.OrbitValidationError— 400/422: the request body failed validation; fix it, don’t retry.OrbitServerError— 5xx after retries exhausted, or persistent network failure.OrbitWebhookSignatureError— webhook verification failed, with acodeofbad_signature | expired | malformed. It extendsError, notOrbitApiError, so a broadcatch (OrbitApiError)block can’t accidentally retry a forged webhook.
Where this page ends and the quickstarts begin
Use the model above to choose a tier, then pick a language:- GA server: Node.js, Web
- Core-scope server: Python, Go, Ruby, PHP, Java, .NET
- Client-scope: Web, Swift, Android, Flutter, React Native