8 min read
Feb 11, 2026

JavaScript / TypeScript SDK

Install and use the official Pivot SDK for JavaScript and TypeScript to interact with the Pivot REST API.

The official Pivot SDK for JavaScript and TypeScript provides a type-safe, ergonomic way to interact with the Pivot REST API. It handles authentication, retries with exponential backoff, and pagination automatically.

Installation

Terminal window
npm install @hellopivot/sdk

Or with your preferred package manager:

Terminal window
pnpm add @hellopivot/sdk
yarn add @hellopivot/sdk

The SDK is ESM-only and requires Node.js 18 or later. It works in any runtime that supports the Fetch API (Node.js, Deno, Bun, Cloudflare Workers, etc.).

Quick Start

1

Create an integration and get your API key

Go to Organization Admin > Integrations and create a new integration. Copy the API key — you’ll need it to authenticate requests. See Getting Started with the Pivot API for detailed instructions.

2

Initialize the client

import Pivot from '@hellopivot/sdk';
const pivot = new Pivot({
apiKey: 'your-api-key',
});

You can also set the PIVOT_API_KEY environment variable instead of passing the key directly:

Terminal window
export PIVOT_API_KEY=your-api-key
import Pivot from '@hellopivot/sdk';
// Automatically reads from PIVOT_API_KEY
const pivot = new Pivot();
3

Make your first API call

// List spaces in an organization
const response = await pivot.spaces.getSpacesByOrganizationId('your-org-id', 0);
console.log(response.spaces);

Available Resources

The SDK organizes API methods into resource namespaces that mirror the API structure:

ResourceDescription
pivot.blocksBlock operations (pages, assignments, etc)
pivot.groupsGroup management
pivot.invitesOrganization invite operations
pivot.labelsLabel CRUD and space label management
pivot.roomsRooms, messages, recordings, members
pivot.spacesSpace CRUD, members, roles, invites
pivot.usersUser lookup and management
pivot.webhooksWebhook CRUD and delivery logs

Every method is fully typed — both the parameters and the response — so you get autocomplete and type safety out of the box.

Configuration

The Pivot constructor accepts an options object:

import Pivot from '@hellopivot/sdk';
const pivot = new Pivot({
// Required: your API key (or set PIVOT_API_KEY env var)
apiKey: 'your-api-key',
// Optional: override the base URL (default: https://api.pivot.app)
baseUrl: 'https://api.pivot.app',
// Optional: request timeout in milliseconds (default: 30000)
timeoutMs: 30_000,
// Optional: retry configuration (or set to false to disable)
retry: {
maxRetries: 3, // Number of retry attempts
initialDelayMs: 500, // Initial delay before first retry
maxDelayMs: 30_000, // Maximum delay between retries
backoffMultiplier: 2, // Exponential backoff multiplier
jitterFactor: 0.25, // Randomness to prevent thundering herd
},
// Optional: additional headers for every request
defaultHeaders: {
'X-Custom-Header': 'value',
},
});

Retries

The SDK automatically retries failed requests with exponential backoff and jitter. By default, the following errors trigger a retry:

  • Network errors (e.g., connection refused, DNS failures)
  • HTTP 408 Request Timeout
  • HTTP 409 Conflict
  • HTTP 429 Too Many Requests
  • HTTP 500 Internal Server Error
  • HTTP 502 Bad Gateway
  • HTTP 503 Service Unavailable
  • HTTP 504 Gateway Timeout

Client errors (4xx other than the above) are not retried since they indicate a problem with the request itself.

The SDK also respects the Retry-After header when present.

To disable retries entirely:

const pivot = new Pivot({
apiKey: 'your-api-key',
retry: false,
});

Pagination

The Pivot API uses two pagination strategies: offset-based and cursor-based. The SDK provides helper functions for both.

Offset-Based Pagination

Used by endpoints like listing spaces, groups, and space members:

import { autoPageOffset } from '@hellopivot/sdk';
const allSpaces = await autoPageOffset(
(offset) => pivot.spaces.getSpacesByOrganizationId(orgId, offset),
(response) => ({
items: response.spaces ?? [],
nextOffset: response.nextOffset,
})
);

Cursor-Based Pagination

Used by endpoints like listing messages and recordings:

import { autoPageCursor } from '@hellopivot/sdk';
const allMessages = await autoPageCursor(
(cursor) =>
pivot.rooms.getRoomMessages(roomId, {
cursorSentAt: cursor?.cursorSentAt,
cursorMessageId: cursor?.cursorMessageId,
}),
(response) => ({
items: response.messages ?? [],
hasMore: response.shardHasMore ?? false,
nextCursor: {
cursorSentAt: response.nextCursorSentAt,
cursorMessageId: response.nextCursorMessageId,
},
})
);

Both helpers accept an options parameter to limit the number of items or pages fetched:

// Fetch at most 100 items
const spaces = await autoPageOffset(fetchPage, extractPage, {
maxItems: 100,
});
// Fetch at most 5 pages
const messages = await autoPageCursor(fetchPage, extractPage, {
maxPages: 5,
});

Error Handling

The SDK provides typed error classes for different failure scenarios:

import Pivot, {
PivotAPIError,
PivotTimeoutError,
PivotAuthenticationError,
} from '@hellopivot/sdk';
try {
const space = await pivot.spaces.getSpacesByOrganizationId(orgId, 0);
} catch (error) {
if (error instanceof PivotAPIError) {
console.error(`API error ${error.status}: ${error.message}`);
console.error('Response body:', error.body);
} else if (error instanceof PivotTimeoutError) {
console.error('Request timed out');
} else if (error instanceof PivotAuthenticationError) {
console.error('Missing or invalid API key');
}
}
Error ClassWhen it’s thrown
PivotAPIErrorAPI returns a non-2xx status code
PivotTimeoutErrorRequest exceeds the configured timeout
PivotRetryErrorAll retry attempts exhausted
PivotAuthenticationErrorNo API key provided

Examples

List all spaces in an organization

const response = await pivot.spaces.getSpacesByOrganizationId(orgId, 0, {
status: 'active',
sortBy: 'created_at_desc',
});
for (const space of response.spaces ?? []) {
console.log(`${space.name} (${space.id})`);
}

Create a space and invite members

// Create a new space
const { space } = await pivot.spaces.createSpace(orgId, {
name: 'Engineering Team',
type: 'SPACE_TYPE_TEAM',
});
// Invite members by email
await pivot.spaces.inviteToSpaceByEmails(orgId, space.id, {
roleIds: ['role-id'],
});

Get room messages with pagination

import { autoPageCursor } from '@hellopivot/sdk';
const messages = await autoPageCursor(
(cursor) =>
pivot.rooms.getRoomMessages(roomId, {
cursorSentAt: cursor?.cursorSentAt,
cursorMessageId: cursor?.cursorMessageId,
limit: 50,
}),
(response) => ({
items: response.messages ?? [],
hasMore: response.shardHasMore ?? false,
nextCursor: {
cursorSentAt: response.nextCursorSentAt,
cursorMessageId: response.nextCursorMessageId,
},
}),
{ maxItems: 200 }
);

Set up a webhook

const { webhook, secret } = await pivot.webhooks.createWebhook(orgId, {
name: 'My Webhook',
endpointUrl: 'https://example.com/webhook',
subscriptions: [
{
subjectType: 'WEBHOOK_SUBJECT_TYPE_ROOM',
subjectId: 'room-uuid',
subscriptionType: 'WEBHOOK_SUBSCRIPTION_TYPE_MESSAGE_SENT',
},
],
});
// Store the secret securely — it's only returned once
console.log('Webhook secret:', secret);

Webhooks

The SDK provides utilities for verifying webhook signatures and parsing webhook payloads with full type safety. See the Webhooks documentation for details on setting up webhooks.

Verifying Signatures

Every webhook delivery includes an X-Pivot-Signature header containing an HMAC-SHA256 signature. Always verify this before processing the event:

import { verifyWebhookSignature } from '@hellopivot/sdk';
app.post('/webhook', (req, res) => {
const isValid = verifyWebhookSignature(
req.body, // raw body string
req.headers['x-pivot-signature'],
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
res.status(200).send('OK');
});

Parsing Events

Use parseWebhookEvent to verify the signature and parse the payload into a typed event in one step:

import { parseWebhookEvent } from '@hellopivot/sdk';
app.post('/webhook', (req, res) => {
try {
const event = parseWebhookEvent(
req.body,
req.headers['x-pivot-signature'],
process.env.WEBHOOK_SECRET
);
switch (event.eventType) {
case 'message_sent':
console.log('New message:', event.data.messageId);
break;
case 'message_deleted':
console.log('Deleted:', event.data.messageId);
break;
case 'message_edited':
console.log('Edited:', event.data.messageId);
break;
case 'room_recording_published':
console.log('Recording ready:', event.data.recordingId);
break;
case 'room_recording_transcript_published':
console.log('Transcript ready:', event.data.recordingId);
break;
case 'block_response_sent':
console.log('Block response sent:', event.data.blockResponseId);
break;
}
res.status(200).send('OK');
} catch (err) {
res.status(401).send('Invalid webhook');
}
});

Webhook Event Types

The SDK provides a discriminated union type WebhookEvent that narrows based on eventType:

Event TypeData Fields
message_sentmessageId, roomId, userId, status, createdAt
message_deletedmessageId, roomId, userId, status, deletedAt
message_editedmessageId, roomId, userId, status, updatedAt
room_recording_publishedrecordingId, roomId, createdAt
room_recording_transcript_publishedrecordingId, roomId, createdAt
block_response_sentblockResponseId, blockId, userId, status, createdAt, richContent, attachments

Every event also includes organizationId, spaceId, subject (type + id), and timestamp.

TypeScript Support

All request and response types are exported from the package:

import type {
Space,
Block,
Room,
Message,
CreateSpaceContent,
GetSpacesByOrganizationIdResponse,
WebhookEvent,
MessageSentEvent,
} from '@hellopivot/sdk';

The SDK is generated from the Pivot OpenAPI specification, so types always match the current API.

Requirements

  • Node.js 18+ (or any runtime with Fetch API support)
  • ESM only — CommonJS is not supported
  • TypeScript 5.0+ recommended (works with JavaScript too)

Was this guide helpful?