npm package: @kaiban/sdk
Description: Official TypeScript SDK for the Kaiban API
Author: Kaiban
Latest version: 0.4.21

# Kaiban TypeScript SDK

Official TypeScript SDK for the Kaiban API.

## Install

```bash
npm install @kaiban/sdk
```

## Quickstart

```ts
import { createKaibanClient } from '@kaiban/sdk';

const client = createKaibanClient({
  tenant: 'your-tenant',
  token: process.env.KAIBAN_TOKEN,
});

const result = await client.agents.list({ limit: 10 });
console.log(result.data); // Array of agents
```

## Auth and Tenancy

- Uses `Authorization: Bearer <token>` automatically when provided
- Sends `x-tenant` header with the configured tenant
- Default base URL: `https://{tenant}.kaiban.io`
- Custom base URL can be configured via `baseUrl` option

## Resources

The SDK provides clients for the following API resources:

### Available Resource Clients

- **agents**: `list`, `listAll`, `get`, `create`, `update`, `createFeedback`, `listSupervisorFeedback`
- **teamMembers**: `list`, `listAll`, `get`
- **cards**: `list`, `listAll`, `get`, `create`, `update`, `delete`, `moveToColumn`, `createActivity`, `createBatchActivities`, `listActivities`, `listAllActivities`
- **activities**: `list`, `listAll`, `create`, `createBatch`
- **teams**: `list`, `listAll`, `get`
- **boards**: `list`, `listAll`, `get`, `create`
- **resources**: `list`, `listAll`, `get`
- **external_channels**: `list`, `listAll`, `get`
- **costs**: `calculateCosts` (offline utility for model cost calculations)

### Pagination

All `list` methods return paginated results with cursor-based navigation:

```ts
import { type Paginated, type Agent, type Card, type ListParams } from '@kaiban/sdk';

// Basic pagination
const params: ListParams = {
  limit: 10,
  order_by: '-created_at',
  filters: { status: 'active' },
};

const result: Paginated<Agent> = await client.agents.list(params);
console.log(result.data); // Array of Agent objects
console.log(result.pagination.next_cursor); // Cursor for next page
console.log(result.pagination.prev_cursor); // Cursor for previous page

// Navigate to next page
if (result.pagination.next_cursor) {
  const nextPage = await client.agents.list({
    limit: 10,
    after: result.pagination.next_cursor,
  });
}

// Navigate backwards
if (result.pagination.prev_cursor) {
  const prevPage = await client.agents.list({
    limit: 10,
    before: result.pagination.prev_cursor,
  });
}

// Filter cards by metadata (cards only)
const cardsResult: Paginated<Card> = await client.cards.list({
  limit: 20,
  metadata: { thread_id: 'thread-123', user_id: 'user-456' },
});
// This becomes: ?metadata.thread_id=thread-123&metadata.user_id=user-456
```

All `listAll` methods return async generators that automatically handle pagination:

```ts
// Iterate through all items automatically
for await (const agent of client.agents.listAll({ limit: 100 })) {
  console.log(agent.id, agent.name);
}
```

## Types

All types are published and re-exported from `@kaiban/sdk`. Available types include:

- **Entities**: `Agent`, `Card`, `Activity`, `Team`, `Board`, `Resource`, `ExternalChannel`, `TeamMember`, `Column`
- **Agent Types**: `AgentFeedback`, `AgentSupervisorFeedback`, `AgentType`, `AgentStatus`, `AgentFeedbackStatus`, `AgentFeedbackType`, `AgentFeedbackEvaluation`
- **Activity Types**: `ActivityCreate`, `ActivityActor`, `ActivityChange`, `ActivityType`
- **Card Types**: `CardStatus`, `CardPart`
- **Board Types**: `Column`
- **Resource Types**: `ResourceStatus`
- **External Channel Types**: `ExternalChannelType`, `ExternalChannelStatus`, `ExternalChannelPriority`
- **Cost Types**: `ModelCostInput`, `ModelCostOutput`
- **A2A Data Parts**: `A2ADataPartType`, `ToolCallStartPart`, `ToolCallArgsPart`, `ToolCallEndPart`, `ToolCallResultPart`, `TaskInfoMessagePart`, `TaskStepOutputPart`, `UserEvaluationPart`, `UserThreadFeedbackPart`, `UserCloseThreadPart`, `GenerateReportPart`, `KaibanActivityPart`, `AgentStatusPart`
- **Request/Response**: `ListParams`, `Paginated`, `PaginationMeta`
- **Configuration**: `KaibanClientConfig`, `RequestOptions`
- **Shared Types**: `ISODate`
- **Error classes**: `HttpError`, `ApiError`, `BadRequestError`, `ValidationError`, `UnauthorizedError`, `ForbiddenError`, `NotFoundError`, `ConflictError`, `RateLimitError`, `UnavailableError`, `ServerError`, `TimeoutError`, `AbortedError`

### Example: Using Types

```ts
import {
  createKaibanClient,
  type Agent,
  type Card,
  type Activity,
  type ActivityCreate,
  type Paginated,
  type ListParams,
  CardStatus,
  ActivityType,
} from '@kaiban/sdk';

const client = createKaibanClient({
  tenant: 'my-tenant',
  token: process.env.KAIBAN_TOKEN!,
});

// Type-safe card creation
// Use Omit<Card, 'id' | 'created_at'> for the input type
const cardData: Omit<Card, 'id' | 'created_at'> = {
  team_id: 'team-123',
  board_id: 'board-123',
  owner_id: 'user-123',
  agent_id: 'agent-123',
  title: 'New task',
  status: CardStatus.BACKLOG,
  column_key: 'inbox',
  priority: 'high',
  member_ids: [],
};

const card: Card = await client.cards.create(cardData);

// Type-safe card update
// Use Partial<Omit<Card, 'id' | 'created_at' | 'updated_at'>>
const updateData: Partial<Omit<Card, 'id' | 'created_at' | 'updated_at'>> = {
  status: CardStatus.DOING,
  priority: 'urgent',
};

const updatedCard: Card = await client.cards.update(card.id, updateData);

// Type-safe activity creation
const activityData: ActivityCreate = {
  board_id: card.board_id,
  team_id: card.team_id,
  card_id: card.id,
  type: ActivityType.CARD_COMMENT_ADDED,
  description: 'Initial comment',
  actor: { id: 'user-1', type: 'user', name: 'John' },
};

const activity: Activity = await client.activities.create(activityData);
```

## Documentation

The SDK includes comprehensive JSDoc documentation for all client methods and types.

### Generate Documentation

To generate the full API documentation:

```bash
npm run docs:generate
```

This will create a `docs` folder with HTML documentation that you can open in your browser.

### Build Documentation

To clean and rebuild the documentation from scratch:

```bash
npm run docs:build
```

### Serve Documentation Locally

To generate and serve the documentation locally:

```bash
npm run docs:serve
```

This will generate the docs and start a local server (requires `npx serve` to be available).

### Clean Documentation

To remove the generated documentation:

```bash
npm run docs:clean
```

## Configuration

The client accepts the following configuration options:

```ts
import { createKaibanClient, type KaibanClientConfig } from '@kaiban/sdk';

const config: KaibanClientConfig = {
  tenant: 'your-tenant', // Required: Your tenant identifier
  token: 'your-api-token', // Required: Your API authentication token
  baseUrl: 'https://custom-host', // Optional: Custom API base URL (default: https://{tenant}.kaiban.io)
  timeoutMs: 30000, // Optional: Request timeout in milliseconds (default: 30000)
  retry: {
    // Optional: Retry configuration
    maxAttempts: 3, // Maximum number of retry attempts (default: 3)
    backoffMs: 1000, // Initial backoff delay in ms (default: 1000)
    maxBackoffMs: 30000, // Maximum backoff delay in ms (default: 30000)
    jitter: true, // Add randomness to backoff (default: true)
    retryOn: [429, 502, 503, 504], // Optional: HTTP status codes to retry on
    retryMethods: ['GET', 'PUT', 'DELETE'], // Optional: HTTP methods to retry
  },
  fetch: globalThis.fetch, // Optional: Custom fetch implementation
  onRequest: ({ method, url, init }) => {
    // Optional: Hook called before each request
    console.log(`Making ${method} request to ${url}`);
  },
  onResponse: ({ status, url, response }) => {
    // Optional: Hook called after each response
    console.log(`Received ${status} from ${url}`);
  },
};

const client = createKaibanClient(config);

// You can also update the token dynamically after client creation
client.setToken('new-token'); // Set a new token
client.setToken(); // Clear the token
```

## Per-call Options

All resource methods accept an optional `RequestOptions` parameter for per-call overrides:

```ts
import { type RequestOptions } from '@kaiban/sdk';

const options: RequestOptions = {
  headers: { 'x-request-id': 'abc-123' }, // Custom headers
  timeoutMs: 10_000, // Override timeout for this request
  signal: new AbortController().signal, // AbortSignal for cancellation
  retry: {
    // Override retry behavior
    maxAttempts: 5,
  },
};

await client.cards.list({ limit: 20 }, options);
await client.agents.get('agent-123', options);
```

## Error Handling

The SDK provides typed error classes for different HTTP status codes and network errors:

```ts
import {
  createKaibanClient,
  ApiError,
  NotFoundError,
  BadRequestError,
  ValidationError,
  UnauthorizedError,
  RateLimitError,
  BadGatewayError,
  GatewayTimeoutError,
  UnavailableError,
  TimeoutError,
  AbortedError,
} from '@kaiban/sdk';

const client = createKaibanClient({
  tenant: 'your-tenant',
  token: process.env.KAIBAN_TOKEN!,
});

try {
  await client.cards.get('non-existent-id');
} catch (err) {
  if (err instanceof NotFoundError) {
    console.error('Card not found:', err.message);
    console.error('Error code:', err.code); // 'not_found'
    console.error('Request ID:', err.meta?.request_id);
    console.error('Timestamp:', err.meta?.timestamp);
  } else if (err instanceof ValidationError) {
    console.error('Validation failed:', err.message);
    console.error('Error code:', err.code); // 'validation_error'
    // ValidationError.details includes issues array
    if (err.details?.issues) {
      err.details.issues.forEach((issue) => {
        console.error(`  - ${issue.path.join('.')}: ${issue.message}`);
      });
    }
  } else if (err instanceof BadRequestError) {
    console.error('Invalid request:', err.message);
    console.error('Error code:', err.code); // 'invalid_argument'
  } else if (err instanceof UnauthorizedError) {
    console.error('Authentication required:', err.message);
    console.error('Error code:', err.code); // 'unauthenticated'
  } else if (err instanceof RateLimitError) {
    console.error('Rate limit exceeded:', err.message);
    console.error('Error code:', err.code); // 'rate_limit_exceeded'
  } else if (err instanceof BadGatewayError) {
    console.error('Bad gateway:', err.message);
    console.error('Error code:', err.code); // 'bad_gateway'
  } else if (err instanceof GatewayTimeoutError) {
    console.error('Gateway timeout:', err.message);
    console.error('Error code:', err.code); // 'gateway_timeout'
  } else if (err instanceof UnavailableError) {
    console.error('Service unavailable:', err.message);
    console.error('Error code:', err.code); // 'service_unavailable'
  } else if (err instanceof TimeoutError) {
    console.error('Request timed out');
  } else if (err instanceof AbortedError) {
    console.error('Request was cancelled');
  } else if (err instanceof ApiError) {
    console.error('API error:', err.message);
    console.error('Error code:', err.code);
    console.error('Details:', err.details);
  } else {
    console.error('Unexpected error:', err);
  }
}
```

### Available Error Classes

- **`ApiError`**: Base class for all API errors (includes `code`, `message`, `details`, `meta` properties)
- **`BadRequestError`**: 400 - Invalid request format or parameters (`invalid_argument`)
- **`ValidationError`**: 422 - Request validation failed (`validation_error`, includes `details.issues[]`)
- **`UnauthorizedError`**: 401 - Authentication required or failed (`unauthenticated`)
- **`ForbiddenError`**: 403 - Insufficient permissions (`permission_denied`)
- **`NotFoundError`**: 404 - Resource not found (`not_found`)
- **`ConflictError`**: 409 - Resource conflict (`conflict`)
- **`RateLimitError`**: 429 - Too many requests (`rate_limit_exceeded`)
- **`BadGatewayError`**: 502 - Bad gateway (`bad_gateway`)
- **`UnavailableError`**: 503 - Service temporarily unavailable (`service_unavailable`)
- **`GatewayTimeoutError`**: 504 - Gateway timeout (`gateway_timeout`)
- **`ServerError`**: 500 - Internal server error (`internal`)
- **`TimeoutError`**: Request exceeded configured timeout
- **`AbortedError`**: Request was cancelled via AbortSignal
- **`HttpError`**: Low-level HTTP error (includes `status`, `url`, `body` properties)

All API errors include:

- `code`: Backend error code (e.g., `validation_error`, `not_found`)
- `message`: Human-readable error message
- `details`: Optional error details (for `ValidationError`, includes `issues[]` array)
- `meta`: Request metadata with `request_id?` and `timestamp`

## Examples by resource

### Agents

```ts
// List agents with pagination
const result = await client.agents.list({
  limit: 20,
  order_by: '-created_at',
});
console.log(result.data); // Array of agents
console.log(result.pagination.next_cursor);

// Iterate through all agents
for await (const agent of client.agents.listAll({ limit: 100 })) {
  console.log(agent.id, agent.name);
}

// Get agent by id
const agent = await client.agents.get('agent-123');

// Create a new agent
const newAgent = await client.agents.create({
  name: 'SupportAgent',
  description: 'Handles support tickets',
  team_id: 'team_123',
  owner_id: 'user_123',
  created_by: 'user_123',
  type: 'a2a',
  status: 'active',
  monthly_budget: 200,
  examples: [],
});

// Update agent
const updated = await client.agents.update('agent-123', {
  name: 'Updated Name',
  description: 'New description',
});

// Create agent feedback
await client.agents.createFeedback('agent-123', {
  team_id: 'team-123',
  activity_id: 'activity-456',
  status: 'unprocessed',
  type: 'user_conversation_feedback',
  evaluation: 'positive',
  comment: 'Excellent performance',
  created_by: 'user-001',
});

// List supervisor feedback for an agent
const feedback = await client.agents.listSupervisorFeedback('agent-123', {
  limit: 10,
  order_by: '-created_at',
});
```

### Teams

```ts
// List teams with pagination
const result = await client.teams.list({
  limit: 20,
  filters: { status: 'active' },
});

// Iterate through all teams
for await (const team of client.teams.listAll()) {
  console.log(team.id, team.name);
}

// Get a specific team
const team = await client.teams.get('team-123');
```

### Team Members

```ts
// List team members with pagination
const result = await client.teamMembers.list({
  limit: 20,
  order_by: '-joined_at',
});

// Iterate through all team members
for await (const member of client.teamMembers.listAll({ limit: 100 })) {
  console.log(member.id, member.user_id, member.role);
}

// Get a specific team member
const member = await client.teamMembers.get('tm-123');
```

### Boards

```ts
// List boards
const result = await client.boards.list({ limit: 10 });

// Iterate through all boards
for await (const board of client.boards.listAll()) {
  console.log(board.id, board.name, board.columns);
}

// Get a specific board
const board = await client.boards.get('board-123');

// Create a new board
const newBoard = await client.boards.create({
  name: 'Sales',
  team_id: 'team_123',
  owner_id: 'user_123',
  created_by: 'user_123',
  description: 'Manage pipeline',
  columns: [],
  // agent_ids, member_ids, and allowed_roles are optional (default to [])
});
```

### Cards

```ts
import { CardStatus, ActivityType } from '@kaiban/sdk';

// List cards with pagination and filters
const result = await client.cards.list({
  limit: 20,
  filters: { status: 'doing', board_id: 'board-123' },
  order_by: '-created_at',
});

// Iterate through all cards
for await (const card of client.cards.listAll({ limit: 100 })) {
  console.log(card.id, card.title);
}

// Create a card with optional activity tracking
const newCard = await client.cards.create(
  {
    team_id: 'team-123',
    board_id: 'board-123',
    owner_id: 'user-123',
    agent_id: 'agent-123',
    title: 'Investigate issue',
    status: CardStatus.BACKLOG,
    column_key: 'inbox',
    priority: 'medium',
    member_ids: [],
  },
  { create_activity: true } // Automatically creates a 'card_created' activity
);

// Get a card
const card = await client.cards.get(newCard.id);

// Update a card
const updatedCard = await client.cards.update(card.id, {
  status: CardStatus.DOING,
  priority: 'high',
});

// Move card to a different column (with automatic activity tracking)
const movedCard = await client.cards.moveToColumn(card.id, 'in_progress', {
  actor: {
    id: 'user-123',
    type: 'user',
    name: 'John Doe',
  },
});

// Delete a card
await client.cards.delete(card.id);

// Create an activity for a card
await client.cards.createActivity(card.id, {
  board_id: card.board_id,
  team_id: card.team_id,
  type: ActivityType.CARD_COMMENT_ADDED,
  description: 'Added a comment',
  actor: { id: 'user-1', type: 'user', name: 'Alice' },
});

// Create multiple activities in batch
await client.cards.createBatchActivities(card.id, [
  {
    board_id: card.board_id,
    team_id: card.team_id,
    type: ActivityType.CARD_COMMENT_ADDED,
    description: 'First comment',
    actor: { id: 'user-1', type: 'user', name: 'Alice' },
  },
  {
    board_id: card.board_id,
    team_id: card.team_id,
    type: ActivityType.CARD_PRIORITY_CHANGED,
    description: 'Changed priority',
    actor: { id: 'user-1', type: 'user', name: 'Alice' },
    changes: [{ field: 'priority', old_value: 'medium', new_value: 'high' }],
  },
]);

// List activities for a card
const activities = await client.cards.listActivities(card.id, {
  limit: 50,
  order_by: '-created_at',
});

// Iterate through all activities for a card
for await (const activity of client.cards.listAllActivities(card.id, { limit: 100 })) {
  console.log(activity.type, activity.description);
}
```

### Activities

```ts
import { ActivityType, type ActivityCreate } from '@kaiban/sdk';

// List all activities with filters
const result = await client.activities.list({
  limit: 50,
  filters: { card_id: 'card-123' },
  order_by: '-created_at',
});

// Iterate through all activities
for await (const activity of client.activities.listAll({ limit: 100 })) {
  console.log(activity.id, activity.type);
}

// Create a single activity
await client.activities.create({
  board_id: 'board-123',
  team_id: 'team-123',
  card_id: 'card-123',
  type: ActivityType.CARD_PRIORITY_CHANGED,
  description: 'Updated priority',
  actor: { id: 'user-1', type: 'user', name: 'Alice' },
  changes: [{ field: 'priority', old_value: 'low', new_value: 'high' }],
});

// Create multiple activities in batch
await client.activities.createBatch([
  {
    board_id: 'board-123',
    team_id: 'team-123',
    card_id: 'card-123',
    type: ActivityType.CARD_COMMENT_ADDED,
    description: 'First comment',
    actor: { id: 'user-1', type: 'user', name: 'Alice' },
  },
  {
    board_id: 'board-123',
    team_id: 'team-123',
    card_id: 'card-123',
    type: ActivityType.CARD_COMMENT_ADDED,
    description: 'Second comment',
    actor: { id: 'user-2', type: 'user', name: 'Bob' },
  },
]);
```

### Resources

```ts
// List resources
const result = await client.resources.list({
  limit: 25,
  order_by: '-created_at',
});

// Iterate through all resources
for await (const resource of client.resources.listAll()) {
  console.log(resource.id, resource.title, resource.status);
}

// Get a specific resource
const resource = await client.resources.get('resource-123');
```

### External Channels

```ts
// List external channels
const result = await client.external_channels.list({ limit: 15 });

// Iterate through all channels
for await (const channel of client.external_channels.listAll()) {
  console.log(channel.id, channel.type, channel.name);
}

// Get a specific channel
const channel = await client.external_channels.get('channel-123');
```

### Model Costs

The `costs` client provides an offline utility to calculate costs for LLM usage based on token counts:

```ts
import { type ModelCostInput } from '@kaiban/sdk';

// Calculate costs for multiple model usages
const usages: ModelCostInput[] = [
  {
    model: 'gpt-4o',
    inputTokens: 1500,
    outputTokens: 500,
    reasoningTokens: 200, // Optional: reasoning tokens for models that support it
  },
  {
    model: 'gpt-4o-mini',
    inputTokens: 3000,
    outputTokens: 1000,
  },
];

const result = client.costs.calculateCosts(usages);

console.log('Total cost:', result.totalCost); // Total cost in dollars
console.log('Total tokens:', result.totalTokens); // Total tokens used

// Get detailed costs per model
result.costsByModel.forEach((modelCost) => {
  console.log(`Model: ${modelCost.model}`);
  console.log(`  Input tokens: ${modelCost.inputTokens}`);
  console.log(`  Output tokens: ${modelCost.outputTokens}`);
  console.log(`  Reasoning tokens: ${modelCost.reasoningTokens}`);
  console.log(`  Charged output tokens: ${modelCost.chargedOutputTokens}`); // output + reasoning
  console.log(`  Total tokens: ${modelCost.totalTokens}`);
  console.log(`  Input cost: $${modelCost.inputCost.toFixed(6)}`);
  console.log(`  Output cost: $${modelCost.outputCost.toFixed(6)}`);
  console.log(`  Total cost: $${modelCost.totalCost.toFixed(6)}`);
});
```

Supported models and their pricing (per 1M tokens):

- **gpt-4o**: $5.00 input, $20.00 output
- **gpt-4o-mini**: $0.60 input, $2.40 output
- **gpt-5**: $1.50 input, $10.00 output
- **gpt-5-mini**: $0.25 input, $2.00 output
- **gpt-5-nano**: $0.05 input, $0.40 output
- **text-embedding-3-small**: $0.02 input, $0.00 output

**Note:** Reasoning tokens (when provided) are billed as output tokens.