SDKs

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.

On this page