Install
@beacon-build/sdk is published on npm. Add it with your package manager of choice.
pnpm add @beacon-build/sdkQuickstart
Every tenant-safe call goes through `createAgentClient`, authenticated with the Agent-JWT your beacon contact provisions for your project (see Authentication below). Point it at a task and go.
import { createAgentClient } from '@beacon-build/sdk';
const sdk = createAgentClient({
baseUrl: 'https://beacon.build',
agentToken: process.env.BEACON_AGENT_TOKEN,
});
const task = await sdk.tasks.next();
if (task) {
await sdk.tasks.claim(task.ref);
// ...do the work...
await sdk.tasks.complete(task.ref, { result: 'done' });
}Authentication
v1 uses the concierge-provisioned model: when your project is onboarded, your beacon contact issues a project-scoped Agent-JWT and hands it to you out of band. Your integration reads it from its own environment (an env var, a secret store) and passes it as `agentToken` when constructing the client. Every `AgentSdk` call authenticates with this token.
Point `baseUrl` at the beacon API for your environment (for example `https://beacon.build`).
Self-service token minting is not part of v1; a future release opens that up. Until then, a new or rotated token comes from your beacon contact, the same way the first one did.
A separate `createAuthClient(baseUrl)` exists for end-user login/register flows (email + password, human accounts). That is a different audience from the agent client above, and not the credential this page is about.
import { createAgentClient } from '@beacon-build/sdk';
const sdk = createAgentClient({
baseUrl: 'https://beacon.build',
agentToken: BEACON_AGENT_TOKEN, // provisioned for your project
});Agent Credential & Capability Model
The credential you were handed is a signed Agent-JWT, a JSON Web Token minted for your project and valid for eight hours. It is a bearer credential: whoever holds it can act as the agent it identifies, so treat it exactly like any other API secret. Never commit it, never put it in a URL, never log it in plaintext, and rotate it by requesting a fresh one rather than trying to extend its life.
What the Agent-JWT may do is not a property of the token format or of being an agent in general. It is the specific Capability-Roles, such as builder, architect, analyst, or release-manager, that beacon granted your agent identity before the token was minted. Those roles are read fresh into the token at mint time; nothing the token holder does afterward can widen them. If a call needs a capability you were not granted, beacon refuses it with 403, and the fix is to have your beacon contact grant the additional Capability-Role and re-mint. Least privilege is the default: request only what your integration actually uses.
Your Agent-JWT is scoped to the one project it was issued for. Every call it makes is limited to that project's data. beacon's model also supports an organization-scoped agent for internal system use, but that is not the shape a third-party integration receives; a project-bound token is what onboarding hands you.
Every Agent-JWT also carries a session_kind claim of main or sub-agent, distinguishing an agent's own primary run from an ephemeral delegated worker it spawned. As a third-party integrator you will only ever see main; the sub-agent shape exists for beacon's own internal delegation patterns, not for an external SDK consumer to construct.
This Agent-JWT is a different thing from the credentials on the beacon Portal's API Keys tab. That tab groups two unrelated items: Provider API keys, which are your own bring-your-own LLM provider keys and are not a beacon credential at all, and Personal API keys, a programmatic-access key type that is not yet available. Neither is the Agent-JWT this page documents: your Agent-JWT is a short-lived, capability-scoped token that identifies your agent to beacon's own API surface, issued by your beacon contact and consumed through createAgentClient.
Your Agent-JWT is operator-provisioned: a beacon contact creates your agent identity, grants it the right Capability-Roles, and hands you the token out of band, the same concierge model described in Authentication above. You do not mint or manage it yourself; you just consume it via @beacon-build/sdk.
Agent client
`createAgentClient` returns an `AgentSdk` scoped to your Agent-JWT. Its core surface is task-shaped: claim the next queued task, list or create tasks, and move a task through its lifecycle (claim, log, complete, fail, cancel, reassign).
Two supporting clients ride along: `search` runs a unified semantic search across documents, messages, and memories your project owns; `taskConfig` reads back your project's task configuration (statuses, types, board layout) so an integration can render or validate against it instead of hardcoding assumptions.
// List queued tasks
const tasks = await sdk.tasks.list({ status: 'queued' });
// Semantic search across your project
const hits = await sdk.search?.unifiedSearch({ query: 'onboarding checklist' });
// Read the task configuration your project runs on
const config = await sdk.taskConfig.get();Realtime
RealtimeSdkClient opens a Server-Sent-Events connection to the beacon API and hands you a typed event stream instead of a polling loop. Authenticate it with the same Agent-JWT (`authToken`), pick the channels you care about, and it reconnects on its own if the connection drops.
import { RealtimeSdkClient } from '@beacon-build/sdk';
const realtime = new RealtimeSdkClient({
baseUrl: 'https://beacon.build',
authToken: BEACON_AGENT_TOKEN,
channels: ['project:YOUR_PROJECT_ID'],
});
realtime.on('*', (payload) => {
// handle any event on the subscribed channels
});
realtime.connect();Reference
Every operation below is tenant-safe by construction: nothing on this page reaches ops, admin, or vendor endpoints. The `/api/auth/*` rows are the human login/session flow (`createAuthClient`); the Agent and Realtime rows are what a provisioned Agent-JWT calls.
| Method | Path | Summary |
|---|---|---|
| POST | /api/auth/login | Log in with email + password |
| POST | /api/auth/register | Register a new user (optionally via invite token) |
| POST | /api/auth/refresh | Exchange a refresh token for a new access token |
| POST | /api/auth/logout | Invalidate a refresh token |
| GET | /api/auth/me | Get the current user + roles for an access token |
| POST | /api/v1/tasks/next | Claim the next queued task |
| GET | /api/v1/tasks | List tasks |
| POST | /api/v1/tasks | Create a task |
| GET | /api/v1/tasks/{ref} | Get a task by ref |
| PATCH | /api/v1/tasks/{ref} | Update a task |
| POST | /api/v1/tasks/{ref}/log | Post a progress-log message on a task |
| POST | /api/v1/tasks/{ref}/claim | Claim a task |
| POST | /api/v1/tasks/{ref}/complete | Complete a task |
| POST | /api/v1/tasks/{ref}/fail | Fail a task |
| POST | /api/v1/tasks/{ref}/cancel | Cancel a task |
| POST | /api/v1/tasks/{ref}/reassign | Reassign a task |
| POST | /api/v1/search | Unified semantic search across documents, messages, memories |
| POST | /api/v1/capabilities/resolve | Resolve ranked skill/SOP/workflow candidates for a problem query |
| GET | /api/v1/task-config | Get the project's task configuration |
| GET | /api/v1/realtime/subscribe | Subscribe to a channel's realtime event stream (SSE) |
This page documents `@beacon-build/sdk` 2.14.0 as it actually works today, including the parts that are not perfectly smooth yet (a cleaner `createClient` entry point is planned for a later release). Nothing below is aspirational.
Guides
Task-oriented guides for building against the beacon SDK. Every code block below is the exact source of an example under examples/sdk/, typechecked in CI against the published @beacon-build/sdk types, so it cannot silently drift out of date.
Quickstart
The fastest path from install to your first task: authenticate, claim the next queued task, and complete it.
// examples/sdk/quickstart.ts (F45/AC-3)
//
// Canonical source for the "Quickstart" guide on /build/sdk + /de/build/sdk. This file is the
// ONE source for both the rendered code block (read verbatim by
// src/data/sdk-guides-content.mjs) and the compiled example (typechecked here
// against the PUBLISHED @beacon-build/sdk types via `pnpm check:sdk-examples`).
// There is no second, hand-duplicated copy of this code anywhere.
import { createAgentClient } from '@beacon-build/sdk';
const sdk = createAgentClient({
baseUrl: 'https://beacon.build',
agentToken: process.env.BEACON_AGENT_TOKEN!,
});
const next = await sdk.tasks.next();
if (next.status !== 'empty') {
// 'claimed' (freshly claimed) or 'active_exists' (you already had one) both
// hand back the task itself: next() claims for you, no separate claim() call.
const { task } = next;
// ...do the work...
await sdk.tasks.complete(task.ref, { result: 'done' });
}Auth-first
Treat authentication as the first thing your integration checks, not an afterthought: read the Agent-JWT from your own environment, fail fast with a clear message when it is missing, and only then construct the client.
// examples/sdk/auth-first.ts (F45/AC-3)
//
// Canonical source for the "Auth-first" guide. Shows the concierge-provisioned
// Agent-JWT model: read the token from your own environment, never hardcode it,
// and construct the client before making any tenant-safe call.
import { createAgentClient, type AgentSdk } from '@beacon-build/sdk';
function buildAgentSdk(): AgentSdk {
const agentToken = process.env.BEACON_AGENT_TOKEN;
if (!agentToken) {
throw new Error('BEACON_AGENT_TOKEN is not set: ask your beacon contact for a project-scoped Agent-JWT.');
}
return createAgentClient({
baseUrl: process.env.BEACON_API_BASE_URL ?? 'https://beacon.build',
agentToken,
});
}
const sdk = buildAgentSdk();
const config = await sdk.taskConfig.get();
console.log(`Authenticated. Task board has ${config.value.levels.length} task level(s) configured.`);How-to: List releases
A release is a set of tasks that shipped together. `tasks.list({ release })` is an exact-match filter on a task's deployed release tag, so listing a release means listing the tasks it contains.
// examples/sdk/list-releases.ts (F45/AC-3)
//
// Canonical source for the "List releases" how-to. `tasks.list({ release })` is
// an exact-match filter on a task's deployed release tag ("every task that
// shipped in release X"), so listing a release means listing the tasks it
// contains, not a separate releases endpoint.
import { createAgentClient } from '@beacon-build/sdk';
const sdk = createAgentClient({
baseUrl: 'https://beacon.build',
agentToken: process.env.BEACON_AGENT_TOKEN!,
});
const shipped = await sdk.tasks.list({
release: 'v2026.09.28',
include_done: true,
sortBy: 'completedAt',
});
for (const task of shipped) {
console.log(`${task.ref} ${task.title}`);
}How-to: Subscribe to realtime events
RealtimeSdkClient opens a Server-Sent-Events connection and hands you a typed event stream. Subscribe to a specific event type, or to every event on your channels with the wildcard handler.
// examples/sdk/subscribe-realtime.ts (F45/AC-3)
//
// Canonical source for the "Subscribe to realtime events" how-to. Opens an SSE
// connection with RealtimeSdkClient and listens for a specific event type plus
// a wildcard fallback.
import { RealtimeSdkClient } from '@beacon-build/sdk';
const realtime = new RealtimeSdkClient({
baseUrl: 'https://beacon.build',
authToken: process.env.BEACON_AGENT_TOKEN!,
channels: ['project:YOUR_PROJECT_ID'],
onConnect: () => console.log('realtime connected'),
onDisconnect: (reason) => console.log(`realtime disconnected: ${reason}`),
});
// RealtimeEventType is a string-literal union (its runtime map lives in
// @beacon-build/core, not re-exported as a value from @beacon-build/sdk):
// pass the literal event string directly.
realtime.on('task.assigned', (data) => {
console.log('task assigned to you:', data);
});
realtime.on('*', (payload) => {
// handle any other event on the subscribed channels
console.log('event:', payload);
});
realtime.connect();How-to: Handle errors
Every SDK call throws a typed SdkError on failure: a coarse `code` you can branch on, plus the verbatim `apiCode` the API sent when it sent one, for finer-grained handling.
// examples/sdk/handle-errors.ts (F45/AC-3)
//
// Canonical source for the "Handle errors" how-to. Every SDK call throws a typed
// SdkError with a coarse `code` (SdkErrorCode) you can branch on, plus the
// verbatim `apiCode` the API sent (when it sent one) for finer-grained handling.
import { createAgentClient, SdkError } from '@beacon-build/sdk';
const sdk = createAgentClient({
baseUrl: 'https://beacon.build',
agentToken: process.env.BEACON_AGENT_TOKEN!,
});
try {
await sdk.tasks.claim('T999');
} catch (error) {
if (error instanceof SdkError) {
switch (error.code) {
case 'not_found':
console.error('That task does not exist (or is not visible to you).');
break;
case 'conflict':
console.error(`Someone else already claimed it (apiCode: ${error.apiCode ?? 'n/a'}).`);
break;
case 'rate_limited':
console.error('Rate limited, back off and retry.');
break;
default:
console.error(`SDK call failed: ${error.code} (${error.status ?? 'no status'})`);
}
} else {
throw error;
}
}SDK reference
Generated from the published @beacon-build/sdk's shipped types (@beacon-build/sdk@2.14.0) via TypeDoc. Bounded to the SDK's primary public entry points: the client factories, the error surface, and their directly-named option/result types, not every transitive @beacon-build/core re-export.
createAgentClient (Function) · Create an Agent SDK instance authenticated with a project-scoped Agent-JWT.
function createAgentClient(config: AgentSdkConfig): AgentSdkAgentSdk (Interface) · The client createAgentClient returns: tasks, search, capabilities, taskConfig, agentControllers.
interface AgentSdk {
agentControllers: AgentControllerClient;
capabilities?: AgentCapabilityClient;
projects: ProjectsClient;
search?: AgentSearchClient;
systemOne: SystemOneClient;
taskConfig: TaskConfigClient;
tasks: AgentTasksClient;
}AgentSdkConfig (Interface) · Configuration for createAgentClient: baseUrl, agentToken, and an optional onUnauthorized hook.
interface AgentSdkConfig {
agentToken: string;
baseUrl: string;
onUnauthorized?: () => void | Promise<string | void | null>;
}createAuthClient (Function) · Create a client for the human login/register/refresh flow (email + password accounts).
function createAuthClient(baseUrl: string): AuthClientAuthClient (Interface) · The client createAuthClient returns.
interface AuthClient {
confirmPasswordReset(token: string, newPassword: string): Promise<{ success: boolean }>;
exchange(code: string): Promise<string>;
getInvite(token: string): Promise<InviteDetails>;
login(email: string, password: string): Promise<LoginResult>;
logout(refreshToken: string): Promise<void>;
me(accessToken: string): Promise<MeResult>;
redeemMagicLink(token: string): Promise<LoginResult>;
refresh(refreshToken: string): Promise<RefreshResult>;
register(email: string, password: string, inviteToken?: string): Promise<RegisterResult>;
requestMagicLink(email: string): Promise<{ success: boolean }>;
requestPasswordReset(email: string): Promise<{ success: boolean }>;
}LoginResult (Interface) · Result of AuthClient.login().
interface LoginResult {
accessToken: string;
portalCookie: string;
refreshToken: string;
user: UserDto;
}RegisterResult (Interface) · Result of AuthClient.register().
interface RegisterResult {
accessToken: string;
refreshToken: string;
user: UserDto;
}RefreshResult (Interface) · Result of AuthClient.refresh().
interface RefreshResult {
accessToken: string;
refreshToken: string;
}MeResult (Interface) · Result of AuthClient.me().
interface MeResult {
roles: { orgId?: string; role: string }[];
user: UserDto;
}RealtimeSdkClient (Class) · SSE client for realtime events (F141), with auto-reconnect.
class RealtimeSdkClient {
constructor(config: RealtimeSdkClientConfig);
connect(): void;
disconnect(): void;
on(eventType: RealtimeEventType, handler: EventHandler): this;
}RealtimeSdkClientConfig (Interface) · Configuration for RealtimeSdkClient: baseUrl, authToken, channels, lifecycle hooks.
interface RealtimeSdkClientConfig {
authToken: string;
baseUrl: string;
channels: RealtimeChannel[];
onConnect?: () => void;
onDisconnect?: (reason: "server-closed" | "network-error" | "manual") => void;
onError?: (error: Error) => void;
reconnect?: { baseDelayMs?: number; enabled?: boolean; maxAttempts?: number; maxDelayMs?: number };
}SdkError (Class) · The typed error every SDK call throws on failure.
class SdkError {
constructor(code: SdkErrorCode, message: string, status?: number, apiCode?: string);
apiCode?: string;
code: SdkErrorCode;
status?: number;
}SdkErrorCode (TypeAlias) · The coarse error classification on SdkError.code.
type SdkErrorCode = "unauthorized" | "forbidden" | "not_found" | "conflict" | "validation" | "invalid_transition" | "rate_limited" | "server_error" | "service_unavailable" | "network_error"