Skip to main content
The TypeScript SDK (@apostra/sdk) is the first Apostra SDK package planned for npm. The Python SDK (apostra) is coming after its model generator is ready. Registry publication and the live synthetic-seller check are still pending, so use the V3 HTTP API until the TypeScript package is published. Both clients expose the same 58 generated operations, including two public document reads, from the OpenAPI 3.1 document. They call HTTP directly and need no MCP session or LLM. TypeScript requires Node.js 22.18+; Python requires 3.11+. For installation and a first campaign preview, see the SDK quickstart.

Release notes

SDK changes are generated and checked against the committed OpenAPI on every pull request. The release workflow can publish TypeScript, Python or both after a reviewer approves the selected package artifacts and their synthetic evidence. Each package compares its proposed version with its own latest published version and will not publish the same or an older version. Removing an operation needs a major version increase, or a minor increase while the SDK is below 1.0.

Make typed calls

After publication:
Python models are typed dictionaries in 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.

Inspect a successful response

Each generated method also has a response variant for support and observability work. TypeScript appends WithResponse; 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; use timeoutMs in TypeScript or timeout in Python to override it.
By default, both SDKs read 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 only the request ID and Retry-After delay while the receipt schema and read-only receipt endpoint are pending. 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:
Python task cancellation propagates into the async helper’s awaited read. Injected providers and transports must cooperate to stop their underlying work.

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.