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
npm install @defiant/sdk# orpnpm add @defiant/sdkQuick 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 projectsconst projects = await client.listProjects();console.log(projects[0].name);
// Create a sprintconst sprint = await client.createSprint(projects[0].id);console.log(sprint.id); // 3fa85f64-...
// Check a diff against active mandatesconst 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 handlingtry { 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 directlyconst 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' levelconst 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: Sprintsprints.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 pagefor await (const sprint of client.sprints.paginate({ projectId })) { console.log(sprint.id, sprint.state);}Next steps
- REST API Reference for the raw HTTP interface
- MCP Server to call Defiant from Claude directly