@sempera/sdk for TypeScript and sempera-sdk for Python. Both are generated from the live OpenAPI document (https://api.sempera.org/openapi.json), so each has one method for every operation the document lists, plus a small hand-written core for authentication, errors, retries, idempotency keys, paging and webhook verification.
They are not published to npm or PyPI yet. Today you install them from the Sempera source tree (04-delivery/api-sdks/ in the repository) or vendor the folder; Web SDK still applies for the browser. The SDKs call only routes that openapi.json lists.
Install
Authentication
Send your API key as a bearer token (Authentication and credentials). Pass it to the constructor, or setSEMPERA_API_KEY and omit it. Keep the key on your server.
createAgent in TypeScript, create_agent in Python. Path and query parameters are named exactly as in openapi.json. A request body is passed as a plain object or dict.
Create and publish an agent
Place a prompt call
createCall / create_call places one outbound call. mode: "prompt" uses only the prompt. agent_id is still the line (number, consent, do-not-contact, calling hours, caps and billing). Send task, and optionally first_sentence, voice and language. Do not send agent_version or production with mode: "prompt". The call still requires Idempotency-Key and consent_basis. Passing your own key makes a retry safe across process restarts.
Errors
Every refusal is the one error envelope (Errors and limits). The SDK raises a typed error that carries the stablecode, the optional reason and field, the request_id to quote to support and the HTTP status. Branch on code or reason, never on the message.
429 (waiting the Retry-After it sends), a 502, a 504, a 503 with code service_unavailable, and a dropped connection. A 503 owner_setup_required is never retried because waiting does not fix it. A write that has no idempotency key is retried only after a 429, which means it was not processed. Change the count with max_retries / maxRetries on the client or on one call.
Pagination
List routes takelimit and cursor and answer {"object": "list", "data": [...], "has_more": true, "next_cursor": "..."} (Errors and limits). paginate follows next_cursor until has_more is false and yields every item.
paginate follows the native envelope only. A compatibility route keeps its own envelope (Errors and limits), so paginate raises on one instead of silently yielding nothing; page those by hand with the cursor it returns.
Idempotency keys
Operations that accept anIdempotency-Key (listed in Errors and limits) get one automatically: the SDK generates a key for the call and sends the same key on every retry, so a retried POST cannot act twice. Pass your own to make a retry safe across process restarts, for instance one derived from your order id. A key lives 24 hours; the same key with a different request is a 409 idempotency_key_reused.
Webhook signature verification
Verify every delivery against the raw request body before you parse it (Webhooks). The helper checks theX-Sempera-Signature header (t=<unix seconds>,v1=<hex HMAC-SHA256 over "<t>." + body>) in constant time and rejects a timestamp more than 300 seconds from your clock. It raises WebhookSignatureError; answer 400 and process nothing. Then drop an event id you have already handled.
Staying in step with the API
The operation layers are regenerated from the contract with one command,python3 06-tools/build-sdks.py, and a test in the Sempera repository fails when a route is added, renamed or retyped without regenerating. GET /v1/me and the API_VERSION constant tell you which contract version your copy was built from.