@apostra/sdk for TypeScript or apostra for Python 3.11 or later.
Both packages are generated from the current V3 OpenAPI 3.1 document,
so the generated operation catalogue stays in step with the API. They call HTTP
directly and need no MCP session or LLM. The generated manifest currently lists
67 public V3 operations.
For installation and a first campaign preview, see the SDK quickstart.
Make typed calls
apostra.models. Input field names
remain the wire names. TypeScript exports named GetDeliveryInput and
GetDeliveryResult types, plus wire models at @apostra/sdk/models. Both
languages return the success envelope’s data directly. Schema patterns,
length limits, conditional requirements and permissions are checked by the API.
For a campaign_delivery response with configured campaign or media-buy goals,
goalProgress describes the matching goal, commitment, achieved value and
verdict. It is limited to the campaigns and media buys in the current result
page. The V3 response intentionally omits V2-only pacing and denominator
details.
Inspect a successful response
Each generated method also has a response variant for support and observability work. TypeScript appendsWithResponse; Python appends _with_response.
These variants keep the normal method’s input and write-idempotency rules, and
return the result data, request ID and response headers (plus HTTP status).
Use the returned request ID to correlate a successful call with support, and
handle headers as potentially sensitive transport metadata rather than logging
them indiscriminately.
Credentials and accounts
Choose exactly one of an API key, an existing REST/M2M access token, or an injected token provider. Providers obtain and refresh tokens in your application; the SDK invokes them for each request and never replays a failed 401 request. The async Python client uses an async provider. MCP-audience OAuth tokens are not REST credentials. See Authentication. For a deployed backend using OAuth client credentials, use the SDK helper with the client ID, client secret and scope returned when the M2M application was created. It retains a token only in memory, refreshes it 60 seconds before expiry, and shares one in-flight refresh between concurrent calls. Keep the client secret in your server-side secrets manager. Do not use this helper in a browser or log its inputs or result. Token refreshes have a 30-second deadline by default; usetimeoutMs in TypeScript or timeout in Python to override it.
APOSTRA_API_KEY, APOSTRA_ACCOUNT_ID and
APOSTRA_BASE_URL from the environment. Explicit constructor options override
the environment values. The base URL must use HTTPS, except for a loopback URL
used in local development.
Keep keys on the server. Do not put them in browser bundles, URLs or logs.
There is no SDK credential store. Requests send a versioned user agent and the
explicit account header, with no additional telemetry request.
A per-call accountId (Python: account_id) overrides the client default.
It selects only an account the credential can resolve and must match that
resolved account. It does not switch an MCP session or expand permissions.
Errors, retries and cancellation
ApostraError exposes HTTP status, code, recovery, retry timing, and the
request ID. Catch typed errors such as RateLimitError and ValidationError;
unknown error codes remain strings, so new server codes stay readable.
Malformed responses raise ProtocolError; transport failures retain the
underlying fetch/httpx exception. Default exception strings omit response text.
Calls time out after 30 seconds and retry transient network, 429 and 5xx
failures twice by default with full-jitter backoff. Set maxRetries (Python:
max_retries) to 0 to opt out. Reads retry automatically. Writes replay only
with their required caller-owned idempotency key; the SDK neither generates nor
replaces it. The server’s retry_after is a minimum delay. Preserve the
operation’s original idempotencyKey, clientRequestId or other
operation-specific key; these fields are not interchangeable.
A 202 raises InFlightReceiptError rather than returning a completed result.
It carries the receipt ID, operation, state, pending reason and Retry-After
delay. Poll GET /api/v3/receipts/{receiptId} with the same credential and
account scope to inspect its current state, or use settle (settle_async for
async Python) to replay the same keyed operation until it settles, with a
deadline and cancellation. It never creates a replacement key.
TypeScript accepts an AbortSignal. Python async calls support task cancellation
and timeouts. Cancelling HTTP does not undo server work. A server mutation must
use the operation’s existing state transition if cancellation is supported.
TypeScript timeouts cover token acquisition, HTTP and response-body consumption.
Async Python timeouts cover the awaited provider and HTTP call. A synchronous
Python provider is caller-bounded: a blocked provider raises TimeoutError
before dispatch, while httpx uses the same timeout for the HTTP call.
Pages and asynchronous work
paginate (paginate_async in async Python) accepts a read callback and a
next-cursor callback. Keep the original query, limit and account fixed. Search
uses objects.nextCursor; delivery uses page.nextCursor. Delivery pages are
live per call, not a snapshot. Document sections also require the original
applicability.asOf value. Cursors are opaque and cannot cross queries.
poll accepts a read callback and completion predicate. Python requires a
timeout; TypeScript requires a cancellation signal. For proposal requests,
keep the original body, revision and idempotency key, and distinguish result
pagination from capability pagination. The helper does not choose business
states or retry failed requests.
TypeScript read callbacks receive the helper’s cancellation signal. Forward it
to each SDK call so cancellation reaches the HTTP request:
Human steps and unsupported helpers
uploadCreativeAsset / upload_creative_asset returns the typed current human
task and fallback_url. Some UI results expose optional openInBrowserUrl;
others expose parameters only. The SDK never invents a URL from those parameters.
Import TypeScript verifyWebhook from the server-only
@apostra/sdk/webhooks subpath; it does not load into the browser-safe SDK root.
verifyWebhook / verify_webhook verifies the raw V3 webhook body with
duplicate-preserving signature, timestamp, and delivery-ID headers. It rejects
duplicate headers, uses constant-time HMAC comparison, enforces the five-minute
window, and selects the secret by key ID so callers can keep both rotation keys.
openHandoff / open_handoff returns an actual HTTPS URL supplied by the
caller. It opens nothing in headless code. Interactive applications can inject
an opener. There is no embedded UI, OAuth login flow, wait/resume protocol, or
byte-upload helper in the current SDK contract.
Those helpers need a public contract before they can be supported.
getHandoff / get_handoff narrows an operation result to a supported typed
handoff (kind, url, expiresAt, resume) or returns null / None.
Pass that value to openHandoff / open_handoff; headless code only returns
the supplied HTTPS URL and interactive applications can inject an opener. A
browser opening or closing is not completion: follow resume and read the
affected object before continuing. The SDK never constructs a URL or embeds an
OAuth login flow.