TypeScript SDK
@outflare/sdk is a small typed client for the Outflare API. Its types are generated from api/openapi.yaml, so every path, parameter and response is checked by the TypeScript compiler, and CI keeps it in step with the server. It is licensed Apache-2.0, so you can embed it in closed-source tools without copyleft obligations.
Not on npm The package is not published to a registry yet. Build it from the repository:
git clone https://github.com/outflareai/outflarecd outflare && pnpm install && pnpm --filter @outflare/sdk build# then, in your project:npm install /path/to/outflare/sdk/tsimport { readFile } from 'node:fs/promises';import { createClient } from '@outflare/sdk';
const gtm = createClient({ baseUrl: 'https://outflare.example.com', token: process.env.OUTFLARE_TOKEN, // oft_...});
const ws = process.env.OUTFLARE_WORKSPACE!;
// List active mailboxesconst { data, error } = await gtm.GET('/v1/workspaces/{ws}/mailboxes', { params: { path: { ws }, query: { status: 'active' } },});if (error) throw new Error(error.error.message);for (const m of data.data) console.log(m.email, m.daily_limit);
// Import a reseller CSV into a poolconst csv = await readFile('mailboxes.csv', 'utf8');const res = await gtm.POST('/v1/workspaces/{ws}/mailboxes/import', { params: { path: { ws }, query: { pool: 'Warm' } }, body: csv, headers: { 'Content-Type': 'text/csv' }, bodySerializer: (b: string) => b,});console.log(res.data); // { created, updated, errors }The client is built on openapi-fetch. Every call returns { data, error, response } instead of throwing, and error has the API’s error shape.
Throwing on errors with ok()
Section titled “Throwing on errors with ok()”Scripts and agents usually want a call to either succeed or stop. Wrap it in ok(): it returns data, or throws an OutflareError with the HTTP status, the API’s error code (such as conflict or invalid_request), the message and the requestId.
import { createClient, ok, OutflareError } from '@outflare/sdk';
const stats = ok(await gtm.GET('/v1/workspaces/{ws}/stats', { params: { path: { ws } } }));console.log(`${stats.sent_24h} sent today of ${stats.mailboxes.daily_capacity}, ${stats.unread_replies} unread replies`);
try { ok(await gtm.DELETE('/v1/workspaces/{ws}/oauth-connections/{id}', { params: { path: { ws, id } } }));} catch (e) { if (e instanceof OutflareError && e.code === 'conflict') console.log(e.message); // mailboxes still use it else throw e;}Options
Section titled “Options”| Option | Description |
|---|---|
baseUrl |
Your instance, for example https://outflare.example.com. Leave empty for same-origin, as the web app does. |
token |
An API token. Omit it in the browser to use the signed-in session cookie. |
fetch |
A custom fetch implementation, for tests or older runtimes. |
mission |
Act inside an agent mission: every call carries Outflare-Mission, and writes may come back held. |
missions |
true for a client that sets the mission header itself (as the MCP server does). Types it like one with mission. |
Safe retries
Section titled “Safe retries”Give a write an Idempotency-Key and retry it freely: a repeat with the same key within 24 hours gets the first answer back instead of running again. See Safe retries.
const key = crypto.randomUUID();const send = () => gtm.POST("/v1/workspaces/{ws}/spend", { params: { path: { ws }, header: { "Idempotency-Key": key } }, body: spend });const res = await send().catch(() => send()); // a network error on the first try is safe to retryMissions
Section titled “Missions”Inside a mission, a write may be answered 202 with a MissionHeld body instead of running: a dry run, or held until a person approves it. isHeld() tells the two apart:
import { createClient, isHeld, ok } from "@outflare/sdk";
const gtm = createClient({ baseUrl, token, mission: missionId });const lead = ok(await gtm.POST("/v1/workspaces/{ws}/leads", { params: { path: { ws } }, body: { email } }));if (isHeld(lead)) console.log(lead.message, lead.mission_action.id);else console.log("created", lead.id);A client made without mission never gets those answers, so its types leave them out.
Common schemas are exported as types, including Workspace, WorkspaceStats, Member, APIToken, Mailbox, OAuthConnection, ImportResult, Lead, LeadList, Suppression, Sequence, Step, Enrollment, Message, InboundMessage, ThreadEntry, Meta and ApiError. The full generated paths and components types are exported too.
Other languages
Section titled “Other languages”Generate a client from your instance’s /v1/openapi.yaml with any OpenAPI 3.1 generator, such as openapi-generator for Python, Go or Java.