Getting Started

Install the SDK, create a client, and make your first API call in under two minutes.

Installation

# One-time: point the @kapable scope at the Kapable registry (anonymous read)
echo '@kapable:registry=https://git.kapable.dev/api/packages/kapable/npm/' >> .npmrc

bun add @kapable/sdk     # or: npm install @kapable/sdk
# One-time: .cargo/config.toml (project or ~/.cargo) — anonymous read
[registries.kapable]
index = "sparse+https://git.kapable.dev/api/packages/kapable/cargo/"

# Cargo.toml
[dependencies]
kapable-sdk = { registry = "kapable", version = "0.2" }

Get a Credential

Sign up (creates your organization and a session), then mint an API key. The key's secret is returned once at creation — store it in an environment variable or secrets manager.

# 1. Sign up — org_slug is required
curl -s -X POST https://api.kapable.ai/v1/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"...","org_name":"Acme","org_slug":"acme"}'
# → { "org_id": "...", "session_token": "kses_...", ... }

# 2. Mint an API key with the session
curl -s -X POST https://api.kapable.ai/v1/auth/api-keys \
  -H "Authorization: Bearer kses_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"my-first-key","scopes":["read"]}'
# → { "id": "...", "key_prefix": "sk_live_...", "secret": "sk_live_..." }  ← shown once
What an API key can reach today

A fresh sk_live_ key authenticates everywhere but is scope-gated: the auth (/v1/me, key management), AI (/v1/providers, proxy), and the entire Data API (/v1/tables, row CRUD — read scope for reads, write for mutations/DDL, default project resolved automatically) return 200, and /v1/board/plans works with a key — but /v1/board/stories, /v1/board/products, and the knowledge module still permission-gate API keys (you'll see 403 permission required (role 'api_key' insufficient)). For those, use a session token (Authorization: Bearer kses_...).

Create a Client

import { KapableClient } from '@kapable/sdk';

const client = new KapableClient({
  baseUrl: 'https://api.kapable.ai',
  apiKey: 'sk_live_...',
});
use kapable_sdk::KapableClient;

let client = KapableClient::new("https://api.kapable.ai")
    .api_key("sk_live_...")
    .build();

Constructor Options

Option Type Description
baseUrl string API base URL (e.g. https://api.kapable.ai)
apiKey string? API key for x-api-key header auth
token string? JWT bearer token (mutually exclusive with apiKey)
timeout number? Request timeout in ms (default: 30000)

First API Call

The SDK exposes typed sub-clients for each service: client.auth, client.ai, client.board, client.store, client.data, client.comms, client.knowledge, client.secrets, client.billing, client.wiki, client.harbor, and client.warrant. Each method maps 1:1 to a REST endpoint. Your first call — verify the key works:

// Who am I?
const me = await client.auth.me();
console.log(`Authenticated as ${me.identity_type} in org ${me.org_id}`);

// List the AI providers your org can proxy to
const { data: providers } = await client.ai.listProviders();
for (const p of providers) {
  console.log(`  ${p.name}`);
}
// Who am I?
let me = client.auth().me().await?;
println!("Authenticated in org {}", me.org_id);

// List the AI providers your org can proxy to
let providers = client.ai().list_providers().await?;
for p in &providers.data {
    println!("  {}", p.name);
}

Pagination

List endpoints return a ListResponse<T> with data and total. Use limit and offset for manual pagination, or the built-in async generators for automatic iteration. (Board examples below need session auth or a bridged key — see the scope note above.)

// Auto-paginate through all stories
for await (const story of client.board.paginateStories({ status: 'active' })) {
  console.log(story.code, story.title);
}
// Rust SDK -- manual pagination
let mut offset = 0;
loop {
    let query = ListStoriesQuery {
        status: Some("active".into()),
        limit: Some(50),
        offset: Some(offset),
        ..Default::default()
    };
    let page = client.board().list_stories(query).await?;
    for story in &page.data {
        println!("{} {}", story.code, story.title);
    }
    offset += page.data.len() as i64;
    if offset >= page.total { break; }
}

Error Handling

All SDK methods throw a KapableError on non-2xx responses. The error includes the HTTP status, a machine-readable code, and a human message.

import { KapableError } from '@kapable/sdk';

try {
  await client.board.getStory('nonexistent');
} catch (err) {
  if (err instanceof KapableError) {
    console.error(`${err.status} ${err.code}: ${err.message}`);
    // 404 not_found: Story not found
  }
}
// Rust SDK
use kapable_sdk::KapableError;

match client.board().get_story("nonexistent").await {
    Ok(story) => println!("Found: {}", story.title),
    Err(KapableError::Api { status, code, message }) => {
        eprintln!("{status} {code}: {message}");
        // 404 not_found: Story not found
    }
    Err(e) => eprintln!("Other error: {e}"),
}

Next Steps