@passband/sdk - v0.0.0
    Preparing search index...

    @passband/sdk - v0.0.0

    @passband/sdk

    Typed TypeScript client for the Passband /api/v1 REST API.

    The client is a thin, dependency-free wrapper over fetch. All request and response shapes are generated from the API's OpenAPI document, so the SDK cannot drift from the server.

    npm install @passband/sdk
    # or: pnpm add @passband/sdk

    Requires Node.js >= 18 (uses the global fetch). Ships ESM + CJS builds and types.

    import { Passband } from '@passband/sdk'

    const pb = new Passband({ token: 'sf_...' })

    // Run the content pipeline
    await pb.pipeline.run()

    // Review the ranked feed
    const { drafts } = await pb.drafts.list({ status: 'pending_review' })

    // Approve the first draft, if any
    const first = drafts?.[0]
    if (first) await pb.drafts.approve(first.id)

    // Manage sources
    await pb.sources.add({ name: 'HN', type: 'hackernews', url: 'https://news.ycombinator.com' })

    With no options, the client reads PASSBAND_TOKEN (and PASSBAND_BASE_URL) from the environment:

    // reads PASSBAND_TOKEN from process.env
    const pb = new Passband()
    const summary = await pb.engagement.summary()

    Constructing without a token is allowed, but the first request throws PassbandAuthError — the SDK never sends an unauthenticated request.

    Constructor options take precedence over environment variables:

    Option Env var Default
    token PASSBAND_TOKEN
    baseUrl PASSBAND_BASE_URL https://passband.ai
    fetch global fetch
    retry disabled

    The token is sent as Authorization: Bearer <token>.

    List endpoints are cursor-paginated. list() returns a single page (with a nextCursor). For drafts, drafts.iterate() returns an async iterator that transparently follows nextCursor:

    for await (const draft of pb.drafts.iterate({ status: 'pending_review' })) {
    console.log(draft.id)
    }

    Retries are opt-in. When enabled, the client retries 429 (honoring Retry-After) and 5xx responses with exponential backoff and full jitter. Other 4xx responses are never retried.

    By default only idempotent methods are retried (GET/PUT/DELETE/PATCH). POST is not retried, to avoid duplicating side effects. If your POST endpoints are idempotent, opt in with retryNonIdempotent: true.

    const pb = new Passband({
    token: 'sf_...',
    retry: { retries: 3, backoff: 250, retryNonIdempotent: false },
    })

    Cancellation via AbortSignal is never retried: the original AbortError (or your signal.reason) is re-thrown unchanged.

    Every non-2xx response throws a typed error: PassbandAuthError (401), PassbandForbiddenError (403), PassbandNotFoundError (404), PassbandRateLimitError (429, with retryAfter), and PassbandServerError (5xx). Network and parse failures throw PassbandNetworkError. All extend PassbandError with status, code, message, body, and requestId.

    import {
    Passband,
    PassbandRateLimitError,
    PassbandNotFoundError,
    PassbandError,
    } from '@passband/sdk'

    const pb = new Passband({ token: 'sf_...' })

    try {
    await pb.drafts.approve('draft_123')
    } catch (err) {
    if (err instanceof PassbandRateLimitError) {
    console.warn(`Rate limited; retry after ${err.retryAfter}s`)
    } else if (err instanceof PassbandNotFoundError) {
    console.warn('Draft not found')
    } else if (err instanceof PassbandError) {
    console.error(err.status, err.code, err.message, err.requestId, err.body)
    } else {
    throw err
    }
    }
    Namespace Methods
    pb.pipeline run, status(runId), stats, runs.list
    pb.drafts list, iterate, get, create, update, approve, post, bulk
    pb.sources list, add, update, remove, test
    pb.voice get, update
    pb.engagement summary, upsert
    pb.experiments list, create

    pipeline.status(runId) has no dedicated API route — it performs an O(n) linear scan of pipeline.runs.list (capped at the most recent ~2000 runs) and throws PassbandNotFoundError when the run isn't found. Prefer paging pipeline.runs.list directly when you already know the run is recent.

    Runnable end-to-end examples live in examples/ — including agent-flow.ts, the canonical loop (run pipeline → ranked feed → approve drafts → add sources). Each reads PASSBAND_TOKEN from the environment; see examples/README.md.

    Generate the static API + REST reference (typedoc + Redocly) into docs/:

    pnpm --filter @passband/sdk run docs   # use `run docs` — bare `pnpm docs` is intercepted by pnpm
    
    pnpm --filter @passband/sdk generate   # regenerate openapi.json + types
    pnpm --filter @passband/sdk build # tsup → ESM + CJS + d.ts
    pnpm --filter @passband/sdk test # vitest (unit + OpenAPI contract)

    Types are generated from app/api/v1/docs/openapi.ts (the single source of truth, also served at /api/v1/docs). Run generate after changing the API.