Skip to content

TypeScript SDK

The @defiant/sdk package provides a typed TypeScript client for the Defiant REST API. It wraps all /api/v1/ endpoints with async methods, handles auto-retry on 5xx and 429, and throws DefiantApiError on failures.

Installation

Terminal window
npm install @defiant/sdk
# or
pnpm add @defiant/sdk

Quick start

import { createClient, DefiantApiError } from '@defiant/sdk';
const client = createClient({
apiKey: process.env.DEFIANT_API_KEY!,
// baseUrl defaults to https://app.defiant.build/api/v1
});
// List all projects
const projects = await client.listProjects();
console.log(projects[0].name);
// Create a sprint
const sprint = await client.createSprint(projects[0].id);
console.log(sprint.id); // 3fa85f64-...
// Check a diff against active mandates
const violations = await client.checkMandate(
'diff --git a/src/auth.ts ...',
projects[0].id,
);
violations.filter(v => v.triggered).forEach(v => console.log(v.message));
// Error handling
try {
await client.listProjects();
} catch (e) {
if (e instanceof DefiantApiError) {
console.error(e.status, e.message); // 401, "Defiant API: 401 — Invalid API key"
}
}

DefiantClient

Constructor

import { DefiantClient, createClient } from '@defiant/sdk';
// Factory function (recommended)
const client = createClient({
apiKey: string; // required — create at app.defiant.build/settings/api
baseUrl?: string; // default: 'https://app.defiant.build/api/v1'
maxRetries?: number; // default: 2 — retries on 5xx/429
});
// Or directly
const client = new DefiantClient({ apiKey: '...' });

All methods return a Promise and throw DefiantApiError on non-2xx responses.

Projects

listProjects

const projects: Project[] = await client.listProjects();

createProject

const project: Project = await client.createProject(
name: string,
description?: string,
);

Sprints

listSprints

const sprints: Sprint[] = await client.listSprints(projectId?: string);

getSprint

const sprint: Sprint = await client.getSprint(id: string);

createSprint

const sprint: Sprint = await client.createSprint(
projectId: string,
mandateSet?: string, // e.g. 'b2b-saas', 'fintech'
);

updateSprintStatus

const sprint: Sprint = await client.updateSprintStatus(id: string, status: string);

Mandates

listMandates

Returns all active mandates in the library.

const mandates: Mandate[] = await client.listMandates();
// Mandate: { id, name, description, enforcement: 'block'|'warn'|'log', category }

checkMandate

Check a unified diff against all active mandates. Returns only triggered mandates plus pass results.

const results: MandateCheckResult[] = await client.checkMandate(
diff: string, // unified diff (git diff format)
projectId?: string, // include project-scoped custom mandates
);
// MandateCheckResult: { mandateId, triggered, action: 'block'|'warn'|'log'|'pass', message? }
// Example: block if any mandate triggered at 'block' level
const blocked = results.filter(r => r.triggered && r.action === 'block');
if (blocked.length > 0) {
throw new Error(`Blocked by: ${blocked.map(r => r.message).join(', ')}`);
}

Inbox

listInbox

const items: InboxItem[] = await client.listInbox(
projectId?: string,
resolved?: boolean, // default: returns all
);
// InboxItem: { id, project_id, sprint_id?, title, body?, priority, resolved, created_at }

resolveInboxItem

const item: InboxItem = await client.resolveInboxItem(id: string);

Error handling

import { DefiantApiError } from '@defiant/sdk';
try {
await client.listProjects();
} catch (e) {
if (e instanceof DefiantApiError) {
console.error(e.status); // HTTP status code
console.error(e.message); // human-readable message
}
}

Auto-retry behavior: The client retries on network errors, 429, and 5xx up to maxRetries times with exponential backoff. Respects Retry-After headers.

client.sprints

sprints.create

const sprint = await client.sprints.create({
projectId: string;
goal: string;
priority?: 'low' | 'normal' | 'high'; // default: 'normal'
tokenBudget?: number; // default: 200000
});
// Returns: Sprint

sprints.get

const sprint = await client.sprints.get(sprintId: string);
// Returns: SprintDetail (includes technicalPlan, agents, prs)

sprints.list

const { data, meta } = await client.sprints.list({
projectId?: string;
state?: 'active' | 'complete' | 'failed' | 'blocked';
limit?: number;
offset?: number;
});

sprints.cancel

await client.sprints.cancel(sprintId: string);

sprints.retry

await client.sprints.retry(sprintId: string, {
fromState?: SprintState;
});

sprints.stream

Returns an async iterator of SprintEvent objects via Server-Sent Events:

for await (const event of client.sprints.stream(sprintId)) {
switch (event.type) {
case 'state.transition':
console.log(`${event.from}${event.to}`);
break;
case 'agent.dispatched':
console.log(`Agent dispatched: ${event.agent}`);
break;
case 'agent.completed':
console.log(`Agent completed: ${event.agent}`);
break;
case 'pr.opened':
console.log(`PR opened: ${event.prUrl}`);
break;
case 'mandate.violated':
console.error(`Mandate violation: ${event.mandateId}${event.message}`);
break;
}
// Break on terminal states
if (['COMPLETE', 'FAILED', 'CANCELLED'].includes(event.state)) break;
}

sprints.waitForComplete

Convenience method that wraps stream and resolves when the sprint reaches a terminal state:

const result = await client.sprints.waitForComplete(sprintId, {
timeout?: number; // ms; default: 7200000 (2 hours)
onEvent?: (event: SprintEvent) => void;
});
if (result.state === 'COMPLETE') {
console.log('Sprint complete!', result.tokensUsed);
} else {
console.error('Sprint failed:', result.failureReason);
}

client.events

events.list

const { data } = await client.events.list({
sprintId?: string;
projectId?: string;
type?: EventType;
agentId?: string;
since?: string; // ISO 8601
before?: string; // ISO 8601
limit?: number;
});

client.inbox

inbox.list

const { data } = await client.inbox.list({
resolved?: boolean; // default: false
priority?: 'critical' | 'high' | 'low';
});

inbox.resolve

await client.inbox.resolve(inboxItemId, {
response: string;
});

client.mandates

mandates.list

const { data } = await client.mandates.list({
projectId?: string;
category?: 'security' | 'quality' | 'compliance' | 'process' | 'architecture';
});

mandates.check

const result = await client.mandates.check({
projectId: string;
mandateId?: string;
files: Record<string, string>; // { 'src/foo.ts': '<contents>' }
});
// result.passed: boolean
// result.violations: MandateViolation[]

Type definitions

type Vertical =
| 'solo-founder'
| 'b2b-saas'
| 'marketplace'
| 'fintech'
| 'healthcare'
| 'pe-portfolio';
type SprintState =
| 'INTAKE'
| 'THINK'
| 'PLAN'
| 'BUILD'
| 'SHIP'
| 'COMPLETE'
| 'BLOCKED'
| 'FAILED'
| 'CANCELLED';
interface Project {
id: string;
name: string;
repo: string;
vertical: Vertical;
branch: string;
deployUrl: string | null;
createdAt: string;
updatedAt: string;
}
interface Sprint {
id: string;
projectId: string;
goal: string;
state: SprintState;
priority: 'low' | 'normal' | 'high';
tokenBudget: number;
tokensUsed: number;
agents: AgentId[];
prs: PR[];
createdAt: string;
updatedAt: string;
}
interface SprintEvent {
id: string;
type: EventType;
sprintId: string;
state: SprintState;
agent?: AgentId;
from?: SprintState;
to?: SprintState;
prUrl?: string;
mandateId?: string;
message?: string;
createdAt: string;
}

Error handling

All SDK methods throw DefiantError on failure:

import { DefiantError } from '@defiant/sdk';
try {
const sprint = await client.sprints.create({ projectId, goal });
} catch (err) {
if (err instanceof DefiantError) {
console.error(err.code); // 'CONDUCTOR_OFFLINE' | 'BUDGET_EXHAUSTED' | ...
console.error(err.message);
console.error(err.status); // HTTP status code
}
}

Pagination helper

// Iterate all sprints for a project, page by page
for await (const sprint of client.sprints.paginate({ projectId })) {
console.log(sprint.id, sprint.state);
}

Next steps