ThunderPhone TypeScript SDK
Node.js 18+. Install npm install @thunderphone/sdk. ESM and CommonJS are supported.
Set THUNDERPHONE_API_KEY in your server's environment, or pass { apiKey } to the
constructor. Keep this key out of browser bundles. Optional baseUrl defaults to
https://api.thunderphone.com (do not append /v1).
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,
pollIntervalMs: 1_000,
});
if (done.status === "failed") throw new Error(done.end_reason ?? "Call failed");
console.log((await api.calls.transcript(call.call_id)).transcripts);
Choose a voice from GET /v1/voices. Outbound calls require an outbound-capable
carrier number, sufficient balance, and the organization's outbound confirmation.
A failed call is a terminal result; polling returns it for the caller to inspect.
waitForCompletion supports signal: AbortSignal and aborts on timeout.
Writes are never retried automatically. Reuse an idempotency key only for the same request.
Every public operation is available through the typed api.client.GET, .POST,
.PUT, .PATCH, and .DELETE methods:
const { data, error, response } = await api.client.GET("/v1/calls", {
params: { query: { limit: 20, offset: 0 } },
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
Helpers throw ThunderPhoneError with status and body for API errors; transport
errors propagate. The low-level client returns { data, error, response }.
Types are exported as paths, operations, and components. Both interfaces send
X-ThunderPhone-Client: sdk-typescript/0.2.0.
Verify webhook and tool-call signatures
Every request ThunderPhone sends to your server carries
X-ThunderPhone-Signature-V2, a timestamped HMAC over the method, path, query and
raw body. verifySignature checks it and rejects requests older than five minutes:
import express from "express";
import { verifySignature } from "@thunderphone/sdk";
app.post("/thunderphone-webhook", express.raw({ type: "*/*" }), (req, res) => {
const ok = verifySignature({
header: req.header("X-ThunderPhone-Signature-V2"),
secret: process.env.THUNDERPHONE_WEBHOOK_SECRET!,
method: req.method,
path: req.originalUrl,
body: req.body,
});
if (!ok) return res.sendStatus(401);
res.sendStatus(204);
});
toleranceSeconds changes the 300-second window. computeSignature builds a
signature for your own tests. The helpers use node:crypto. See
Verify webhook signatures.
API reference ·
OpenAPI
Development: npm ci, npm test, npm run typecheck. Regenerate from the repository
root with scripts/generate-sdks.sh; never hand-edit src/schema.ts.