TypeScript SDK
Integrate with FortyOne using generated TypeScript types and secure transport helpers.
Use @fortyone/sdk to call the FortyOne v1 API with generated request and
response types. The client adds bearer authentication, targets the production
API by default, retries safe reads, and includes helpers for pagination,
idempotency, structured errors, and webhook verification.
Preview availability
The TypeScript SDK is not yet available from the public npm registry. The examples below show the current preview API; installation instructions will be added with the first public release.
Create a client and list stories
import { createFortyOneClient, paginateStories } from "@fortyone/sdk";
const token = process.env.FORTYONE_TOKEN;
const workspaceId = process.env.FORTYONE_WORKSPACE_ID;
if (!token || !workspaceId) {
throw new Error("FORTYONE_TOKEN and FORTYONE_WORKSPACE_ID are required");
}
const client = createFortyOneClient({ token });
for await (const story of paginateStories(client, workspaceId, {
limit: 100,
})) {
console.log(story.id, story.reference);
}createFortyOneClient targets https://api.fortyone.app unless you explicitly
override baseUrl for an approved environment. workspaceId is not a URL or a
client setting. It identifies the workspace whose resources you want to access
because FortyOne API operations are workspace-scoped.
Create a story idempotently
Create one idempotency key for each logical write and persist it with the request before the first attempt. Reusing the same key and request makes an ambiguous network retry safe; creating a new key represents a new operation.
import {
apiErrorFromResponse,
createIdempotencyKey,
type CreateStoryRequest,
} from "@fortyone/sdk";
const teamId = process.env.FORTYONE_TEAM_ID;
if (!teamId) {
throw new Error("FORTYONE_TEAM_ID is required");
}
const idempotencyKey = createIdempotencyKey();
const request: CreateStoryRequest = {
title: "Investigate latency",
teamId,
};
// Persist idempotencyKey and request before the first network attempt.
const result = await client.POST("/api/v1/workspaces/{workspaceId}/stories", {
params: {
path: { workspaceId },
header: { "Idempotency-Key": idempotencyKey },
},
body: request,
});
if (result.error !== undefined) {
throw apiErrorFromResponse(result.response, result.error);
}
console.log(result.data.data.id, result.data.data.reference);createIdempotencyKey returns 64 hexadecimal characters generated with
crypto.getRandomValues. Use validateIdempotencyKey when restoring a retained
key. Keep the SDK version, object property order, and values unchanged across
attempts because the API compares the exact serialized JSON bytes.
The create operation accepts a personal access token or service-account key
with stories:write. Use separate clients when reads and writes use different
least-privilege credentials.
Safe reads use a maximum of three attempts by default. Set retry: false to
disable retries or provide a bounded retry policy. The SDK never retries writes.
For an ambiguous network failure, idempotency_in_progress, 429, or a 503
with Retry-After, retain and reuse the same key and request, honor the delay,
and apply a bounded retry budget. See Idempotent writes for the
complete decision table.
Verify a webhook
Read the unmodified request bytes once and verify them before parsing JSON:
import { verifyWebhook } from "@fortyone/sdk";
const secret = process.env.FORTYONE_WEBHOOK_SECRET;
if (!secret) {
throw new Error("FORTYONE_WEBHOOK_SECRET is required");
}
const body = new Uint8Array(await request.arrayBuffer());
await verifyWebhook({
secret,
body,
webhookId: request.headers.get("Webhook-Id") ?? "",
webhookTimestamp: request.headers.get("Webhook-Timestamp") ?? "",
webhookSignature: request.headers.get("Webhook-Signature") ?? "",
});Record and deduplicate Webhook-Id durably before returning 2xx. Do not log
the token, signing secret, signature, or raw payload.