---
title: "Python and TypeScript SDKs"
description: "Typed ThunderPhone clients for agents, calls, and the public API."
---

The ThunderPhone SDKs use the [OpenAPI specification](https://thunderphone.com/openapi.json)
for request and response types. They read `THUNDERPHONE_API_KEY` from your server's
environment. Create a key in the dashboard under **Organization → Keys**, and keep
it in your secret manager. Never include it in browser code.

## TypeScript

Requires Node.js 18 or later. Both ESM and CommonJS are supported.

```bash
npm install @thunderphone/sdk
```

```typescript
import { ThunderPhone } from "@thunderphone/sdk";

const api = new ThunderPhone();
const agent = await api.agents.create({
  name: "Reception",
  prompt: "Help callers with scheduling and business information.",
  voice: process.env.THUNDERPHONE_VOICE!,
});
const call = await api.calls.place({
  agent_id: agent.id,
  from_number: process.env.FROM_NUMBER!,
  to_number: process.env.TO_NUMBER!,
  idempotency_key: "appointment-123",
});
const done = await api.calls.waitForCompletion(call.call_id, { timeoutMs: 300_000 });
if (done.status === "failed") throw new Error(done.end_reason ?? "Call failed");
console.log((await api.calls.transcript(call.call_id)).transcripts);
```

Pass `{ apiKey, baseUrl }` to configure the client explicitly. `baseUrl` defaults
to `https://api.thunderphone.com`, without `/v1`. All operations are available via
`api.client.GET`, `.POST`, `.PUT`, `.PATCH`, and `.DELETE` with typed paths and bodies.
`waitForCompletion` accepts an `AbortSignal`, `timeoutMs`, and `pollIntervalMs`.

## Python

Requires Python 3.10 or later.

```bash
pip install thunderphone
```

```python
import os
from thunderphone import ThunderPhone, AgentRequest, PlaceCallRequest

with ThunderPhone() as api:
    agent = api.agents.create(AgentRequest(
        name="Reception",
        prompt="Help callers with scheduling and business information.",
        voice=os.environ["THUNDERPHONE_VOICE"],
    ))
    call = api.calls.place(PlaceCallRequest(
        agent_id=agent.id,
        from_number=os.environ["FROM_NUMBER"],
        to_number=os.environ["TO_NUMBER"],
        idempotency_key="appointment-123",
    ))
    done = api.calls.wait_for_completion(call.call_id, timeout=300)
    if done.status == "failed":
        raise RuntimeError(done.end_reason)
    print(api.calls.transcript(call.call_id).transcripts)
```

Pass `api_key=` and `base_url=` to configure the client explicitly. Every operation
also has typed sync and async functions under `thunderphone.generated.api`, with
models under `thunderphone.generated.models`. Pass `client=api.client` to use the
same authentication and client-identification header.

## Calls and errors

Choose `THUNDERPHONE_VOICE` from `GET /v1/voices`. Set `FROM_NUMBER` to an
outbound-capable number connected to your carrier, and `TO_NUMBER` to your test
destination. Outbound calling requires sufficient balance and the organization's
outbound confirmation. ThunderPhone-provisioned demo numbers are inbound only.

Polling returns completed or failed calls; inspect the status before consuming
the transcript. Helpers raise `ThunderPhoneError` for unsuccessful HTTP responses
and expose `status` and `body`. Transport errors propagate. Requests that mutate
state are not retried automatically. Reuse an idempotency key only for the same
outbound-call request.

For direct tool use from a coding agent, connect to the
[MCP server](https://api.thunderphone.com/v1/mcp). See the
[coding-agent guide](/guides/coding-agents) for setup and the
[OpenAPI specification](https://thunderphone.com/openapi.json) for every operation.
