@openfactory/sdk
v0.2.4
Published
Typed client for the complete openfactory GraphQL API and event ingestion.
Readme

OpenFactory TypeScript SDK
The official, type-safe TypeScript client for the complete OpenFactory GraphQL
API and API-channel event ingestion. It has zero runtime dependencies and works
in Node.js 18+, browsers, and edge runtimes wherever fetch is available.
For guides and product documentation, visit openfactory.build/docs. For every generated GraphQL operation and its SDK path, see the GraphQL API reference.
Installation
Install @openfactory/sdk with your preferred package manager:
npm
npm install @openfactory/sdkpnpm
pnpm add @openfactory/sdkBun
bun add @openfactory/sdkYarn
yarn add @openfactory/sdkQuickstart
Create a client for your factory and call any generated GraphQL operation:
import { OpenFactory } from "@openfactory/sdk";
const factory = new OpenFactory({
baseUrl: "https://factory.example.com",
apiKey: process.env.OPENFACTORY_API_KEY!,
teamId: process.env.OPENFACTORY_TEAM_ID,
});
const environments = await factory.environments.list();
const task = await factory.tasks.create({
title: "Fix checkout retries",
});
if (task.taskId) {
const checks = await factory.tasks.checks.list({ taskId: task.taskId });
console.log(checks);
}When an operation accepts teamId, the configured default is inserted
automatically. You can override it for an individual call:
const environments = await factory.environments.list({
teamId: "team_other",
});Authentication
OpenFactory supports different credentials for GraphQL and ingestion. Most server-to-server integrations only need an API key.
| Option | Used for | Authorization header |
| --- | --- | --- |
| apiKey | GraphQL and permitted ingestion calls | ApiKey ofk_... |
| accessToken | GraphQL operations that require a human Auth0 session | Bearer ... |
At least one credential is required. If several are provided, the SDK chooses the most specific credential for each transport.
API key permissions
API keys use AWS-style action grants:
*grants full access.openfactory:<rootField>grants one GraphQL operation.openfactory:*grants all GraphQL operations.ingestion:events.createpermitsissues.createandevents.emit.ingestion:*grants all ingestion actions.
For example, create an ingestion-only key for an event forwarder:
const key = await factory.apiKeys.create({
name: "event-forwarder",
permissions: ["ingestion:events.create"],
});Send issues and typed events
Use issues.create to send a plain request into OpenFactory:
const { issueId } = await factory.issues.create({
title: "Checkout returns 500 on Safari",
body: "The failure started after the latest deploy.",
authorName: "Sentry",
externalId: "sentry-123",
});externalId is an idempotency key. Redelivering the same value deduplicates the
request instead of opening another issue.
For named events, parameterize the client with the events declared by your blueprints. TypeScript then validates both the event name and its payload:
type Events = {
CustomerTicket: {
ticketId: string;
subject: string;
body: string;
};
DeployFailed: {
deployId: string;
project: string;
url: string;
};
};
const factory = new OpenFactory<Events>({
baseUrl: "https://factory.example.com",
apiKey: process.env.OPENFACTORY_API_KEY!,
});
await factory.events.emit("CustomerTicket", {
ticketId: "T-42",
subject: "Can't export CSV",
body: "The export button never finishes.",
});The SDK sends the event name and payload as metadata.event and
metadata.payload, where blueprint sensors and trigger filters can use them.
The event name is also used as the issue title unless you provide an override:
await factory.events.emit(
"DeployFailed",
{
deployId: "dep_123",
project: "storefront",
url: "https://deployments.example.com/dep_123",
},
{
title: "Storefront production deploy failed",
externalId: "dep_123",
},
);List ingested requests
Request history uses explicit page-based pagination and supports user and date filters:
const result = await factory.requests.list({
externalId: "user_42",
from: new Date("2026-07-01T00:00:00Z"),
to: new Date("2026-08-01T00:00:00Z"),
page: 1,
limit: 50,
});
console.log(result.requests);
console.log(result.total, result.hasMore);Pages are 1-based, the default page size is 20, and the maximum page size is 100.
GraphQL namespaces
The generated client organizes the complete GraphQL schema into domain-oriented paths, including:
factory.environments.*,factory.tasks.*, andfactory.issues.*factory.automations.*,factory.featureRequests.*, andfactory.taskQueue.*factory.blueprints.*,factory.repos.*, andfactory.pullRequests.*factory.teams.*,factory.users.*, andfactory.apiKeys.*factory.agents.*,factory.channels.*, andfactory.slack.*factory.qualityAssurance.*andfactory.serviceIntegrations.*
Each method accepts typed variables and returns the GraphQL root field directly,
without a { data: { ... } } envelope. The complete, operation-by-operation
list is in GRAPHQL_API.md.
Raw GraphQL
Use the raw escape hatch for custom queries or operations introduced after your installed SDK version:
const data = await factory.raw.graphql<{
getTeams: Array<{ id: string; name: string }>;
}>(`
query Teams {
getTeams {
id
name
}
}
`);
console.log(data.getTeams);Configuration
const factory = new OpenFactory({
baseUrl: "https://factory.example.com",
apiKey: process.env.OPENFACTORY_API_KEY!,
teamId: process.env.OPENFACTORY_TEAM_ID,
maxRetries: 2,
timeoutMs: 10_000,
});| Option | Description | Default |
| --- | --- | --- |
| baseUrl | OpenFactory origin. Required. | — |
| apiKey | ofk_... API key for GraphQL and authorized ingestion. | — |
| accessToken | Human access token for GraphQL. | — |
| teamId | Default team for GraphQL operations that accept teamId. | — |
| graphqlUrl | Override for the GraphQL endpoint. | {baseUrl}/graphql |
| maxRetries | Retries after network errors or retryable responses. | 2 |
| timeoutMs | Timeout for each request attempt, in milliseconds. | 10_000 |
| fetch | Custom fetch implementation for tests or polyfills. | globalThis.fetch |
Retries and errors
The SDK retries network errors and HTTP 408, 429, 500, 502, 503, and
504 responses with exponential backoff. Authentication, validation, and other
non-retryable errors fail immediately.
All failed responses and GraphQL errors are surfaced as OpenFactoryError:
import { OpenFactoryError } from "@openfactory/sdk";
try {
await factory.environments.list();
} catch (error) {
if (error instanceof OpenFactoryError) {
console.error(error.message);
console.error(error.status);
console.error(error.body);
}
}status is the HTTP status when one is available. GraphQL errors returned in a
successful HTTP response use status 200, with the GraphQL error list exposed
through body.
Runtime and module support
@openfactory/sdk ships both ESM and CommonJS builds with bundled TypeScript
declarations. It requires Node.js 18+ or another runtime with a standards-based
fetch implementation.
// ESM / TypeScript
import { OpenFactory } from "@openfactory/sdk";// CommonJS
const { OpenFactory } = require("@openfactory/sdk");Development
From packages/sdk/typescript:
npm install
npm run generate:graphql
npm run typecheck
npm test
npm run buildgenerate:graphql regenerates the TypeScript operations and
GRAPHQL_API.md from the current OpenFactory GraphQL schema.
License
MIT
