> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript and Python SDKs

> Typed Apostra HTTP calls, account selection and error handling.

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](/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](/v2/v3-api-3.1.yaml). 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](/v3/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:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Apostra } from '@apostra/sdk'

  const api = new Apostra()
  const status = await api.getStatus()
  const delivery = await api.getDelivery({
    report: 'campaign_delivery',
    filters: { campaignId: 'your-campaign-id' },
  })
  ```

  ```python Python theme={null}
  from apostra import Apostra

  with Apostra() as api:
      status = api.get_status()
      delivery = api.get_delivery({
          'report': 'campaign_delivery',
          'filters': {'campaignId': 'your-campaign-id'},
      })
  ```
</CodeGroup>

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.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const response = await api.getStatusWithResponse()
  const requestId = response.requestId
  const correlationId = response.headers.get('x-request-id')
  ```

  ```python Python theme={null}
  response = api.get_status_with_response()
  request_id = response['request_id']
  correlation_id = response['headers']['x-request-id']
  ```
</CodeGroup>

## 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](/v3/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.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const api = new Apostra({
    tokenProvider: m2mTokenProvider({ clientId, clientSecret, scope: 'interchange:read' }),
  })
  ```

  ```python Python theme={null}
  api = Apostra(token_provider=m2m_token_provider(
      client_id=client_id, client_secret=client_secret, scope='interchange:read',
  ))
  ```
</CodeGroup>

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:

```typescript theme={null}
const signal = AbortSignal.timeout(30_000)
for await (const page of paginate(
  (cursor, signal) => api.search({ kind: 'inventory_source', limit: 20, cursor }, { signal }),
  page => page.objects?.nextCursor,
  signal,
)) {
  // Consume the page without changing the original query or account.
}
```

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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.