ThunderPhone 2.0 is live.Self-serve, from 2¢/min.Read the announcement

Overview

Python and TypeScript SDKs

Typed ThunderPhone clients for agents, calls, and the public API.

The ThunderPhone SDKs use the OpenAPI specification 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.

npm install @thunderphone/sdk
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.

pip install thunderphone
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.

Verify webhook and tool-call signatures

Both SDKs (0.2.0 or later) verify the X-ThunderPhone-Signature-V2 header that every webhook and tool request carries. The helpers reject requests signed more than 5 minutes ago and accept a list of secrets while you rotate. Pass the raw body and the path with its query string exactly as received.

Python
from thunderphone import verify_signature
 
ok = verify_signature(
    header, secret=SECRET, method="POST", path="/hooks/thunderphone", body=raw_body
)
TypeScript
import { verifySignature } from "@thunderphone/sdk";
 
const ok = verifySignature({
  header, secret: SECRET, method: "POST", path: "/hooks/thunderphone", body: rawBody,
});

The signed string, a worked example and framework wiring are in Verify webhook signatures.

Calls and errors

Choose THUNDERPHONE_VOICE from GET /v1/voices. Set FROM_NUMBER to an outbound-enabled number, and TO_NUMBER to your test destination. Outbound calling requires sufficient balance and the organization's outbound confirmation. Customer VoIP numbers continue to support outbound calling. ThunderPhone-provisioned numbers start inbound-only; an organization admin can request outbound calling from the Phone Numbers page, where each request is reviewed manually.

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. See the coding-agent guide for setup and the OpenAPI specification for every operation.