TypeScript SDK
@opensequence/sdk: install, configure, every resource with an example, and webhook verification.
npm install @opensequence/sdkimport { OpenSequence, OpenSequenceAdmin, OpenSequenceError, verifyWebhook } from '@opensequence/sdk';
const oseq = new OpenSequence({ baseUrl: 'https://sequences.example.com', apiKey: process.env.OSEQ_KEY! });
await oseq.senders.upsert('u_42', { name: 'Max Rivera', email: 'max@acme.com', timezone: 'America/New_York' });
await oseq.contacts.upsert('p_7', { first_name: 'Dana', email: 'dana@example.com' });
const { enrollments } = await oseq.sequences.enroll('seq_01J9…', { contacts: ['p_7'], sender: 'u_42' });A typed, fetch-based client with zero runtime dependencies (global fetch and node:crypto), Node 20 or newer, ESM. The types come from the server's OpenAPI file, so a route that does not exist cannot be called.
Configuration
| Option | Meaning |
|---|---|
baseUrl | your instance, without /v1 |
apiKey | a project key sk_live_… |
fetch | a fetch implementation (tests inject one) |
timeoutMs | abort after this long; default 30,000, 0 disables |
userAgent | sent as user-agent; default opensequence-sdk/<version> |
oseq.http is the HTTP core for an endpoint the SDK has no method for yet: oseq.http.request({ method, path, query, body }).
Errors
Every failure, from the API or on the way to it, is one OpenSequenceError with status (0 when nothing arrived), code (the API's stable code, or NETWORK_ERROR, TIMEOUT, INVALID_RESPONSE), message, details and requestId.
try {
await oseq.sequences.enroll(seqId, { contacts, sender });
} catch (err) {
if (err instanceof OpenSequenceError && err.is('ALREADY_IN_SEQUENCE')) {
const { conflicts } = err.details as { conflicts: Array<{ external_id: string; sequence_name: string }> };
// show them; nobody was enrolled
} else throw err;
}Lists
List methods return one page, { items, nextCursor }, and take { limit, cursor, updated_since } plus the endpoint's filters. listAll() walks every page as an async iterator.
const page = await oseq.enrollments.list({ sequence: seqId, status: 'active', limit: 200 });
for await (const task of oseq.tasks.listAll({ sender: 'u_42', status: 'open' })) render(task);Resources
Webhooks
verifyWebhook({ secret, headers, body }) checks the Standard Webhooks signature and timestamp and returns the parsed event, or throws WebhookVerificationError. Pass the raw body, and an array of secrets during a rotation. signWebhook signs a test event. Details and framework examples: Verifying.
import { verifyWebhook } from '@opensequence/sdk';
const event = verifyWebhook({ secret: process.env.OSEQ_WEBHOOK_SECRET!, headers: req.headers, body: rawBody });
switch (event.type) {
case 'enrollment.exited': /* event.data.enrollment, event.data.exit_reason */ break;
case 'task.created': /* event.data.task */ break;
}Types
Every request and response type is exported (Contact, Enrollment, Journey, Task, WebhookEvent<T>, …), plus paths from the generated OpenAPI types for anything else.