TypeScript SDK
A typed client generated from the API contract, with pagination, idempotency keys and error handling built in.
The SDK is @showpilot/sdk, generated from the same OpenAPI file the reference pages come from, so its types and methods match the API exactly. One namespace per section of the reference, one method per endpoint.
It is not on npm yet. Until it is, it lives in the ShowPilot repository as a workspace package; ask us for access and add it as a workspace dependency, or copy packages/sdk/src into your codebase (two files plus the generated client, no runtime dependencies).
Setup
import { ShowPilotClient } from '@showpilot/sdk';
const client = new ShowPilotClient({ apiKey: process.env.SHOWPILOT_API_KEY! });Options: baseUrl (default https://api.showpilot.com/v1), fetch (bring your own), retryReads (default true: reads retry on 429 up to three times, honouring Retry-After; writes are never retried).
Reads
const { items: tours } = await client.tours.listTours({ status: 'published', limit: 50 });
const tour = await client.tours.getTour(tours[0].id);
console.log(tour.document.screens.length);
const visitor = await client.visitors.getVisitor('b0000001-0000-4000-8000-000000000000');
for (const c of visitor.journey.conversations) console.log(c.startedAt, c.pageUrl);Pages
paginate walks every page of any list method; paginateEvents drains the event log from a since cursor.
import { paginate, paginateEvents } from '@showpilot/sdk';
for await (const lead of paginate(client.leads.listLeads, { limit: 200 })) {
console.log(lead.email, lead.verdict?.tier);
}
for await (const event of paginateEvents(client.events.listEvents, { since: lastSeen })) {
handle(event);
}Writes
Every mutating call sends an Idempotency-Key. The SDK mints a random one when you do not pass idempotencyKey, so a retry you make yourself is safe as long as you reuse it.
const hook = await client.webhooks.createWebhook(
{ url: 'https://hooks.example.com/showpilot', events: ['meeting.booked', 'lead.identified'] },
{ idempotencyKey: 'setup-2026-09-25' },
);
save(hook.secret); // shown only here
await client.followUps.answerFollowUp(followUpId, { answer: 'Yes, SCIM is included on Enterprise.' });Errors
Any non-2xx answer throws ShowPilotApiError with status, code, message and requestId, plus retryAfterSec on a 429, currentVersion on a 409 stale, and problems on a 400 lint-failed.
import { ShowPilotApiError } from '@showpilot/sdk';
try {
await client.leads.updateLead(id, { ownerUserId: repId });
} catch (err) {
if (err instanceof ShowPilotApiError && err.code === 'insufficient-scope') {
// the key needs leads:write
}
throw err;
}client.rateLimit holds the limit, remaining and reset values from the last response.
Streams
client.events.streamEvents({ since }) returns the raw Response for the server-sent event stream; read its body with your SSE parser of choice. See Webhooks and events for the envelope.
Regenerating
After the contract changes: pnpm -F @showpilot/sdk generate. A test diffs the committed client against the spec, so a stale client fails the gate.