JSPM

  • Created
  • Published
  • Downloads 80
  • Score
    100M100P100Q33931F
  • License Apache-2.0

Official TypeScript SDK for the ListBee API — one API call to sell and deliver digital content.

Package Exports

  • listbee

Readme

listbee

npm CI License

Official TypeScript SDK for the ListBee API — one API call to sell and deliver digital content.

  • Zero runtime dependencies (native fetch, Node >= 18)
  • Types generated from OpenAPI spec — zero drift
  • Full error hierarchy (RFC 9457)
  • Cursor-based pagination
  • Retry with exponential backoff
  • Idempotency support

Install

npm install listbee
pnpm add listbee

Quick start

import { ListBee, Deliverable, CheckoutField } from 'listbee';

const client = new ListBee({ apiKey: 'lb_...' });

// Create a listing, attach a deliverable, and publish in one call
const listing = await client.listings.createComplete({
  name: 'SEO Playbook',
  price: 2900, // $29.00 in cents
  description: 'A comprehensive guide to modern SEO.',
  deliverables: [
    Deliverable.url('https://example.com/seo-playbook.pdf'),
  ],
});

const published = await client.listings.publish(listing.id);
console.log(published.url); // https://buy.listbee.so/r7kq2xy9

Or read from the environment:

export LISTBEE_API_KEY="lb_..."
const client = new ListBee(); // reads LISTBEE_API_KEY automatically

Authentication

Pass your API key explicitly or via the LISTBEE_API_KEY environment variable.

const client = new ListBee({ apiKey: 'lb_...' });
const client = new ListBee(); // env var

The key is validated lazily — an AuthenticationError is raised only when you make the first API call. API keys start with lb_. Get yours at console.listbee.so.

Getting an API key programmatically

Agents can bootstrap access without a dashboard — 3 steps:

import { ListBee } from 'listbee';

// No API key needed for auth endpoints
const client = new ListBee({ apiKey: '' });

// Step 1: Send OTP to the user's email
await client.signup.sendOtp({ email: 'user@example.com' });

// Step 2: Verify with the code from email → short-lived access token (at_, 24h)
const session = await client.signup.verify({ email: 'user@example.com', code: '482910' });
// session.access_token = "at_..." (temporary)
// session.is_new = true (first time) or false (returning)

// Step 3: Use access token to create a permanent API key
const authedClient = new ListBee({ apiKey: session.access_token });
const key = await authedClient.apiKeys.create({ name: 'My Agent' });
// key.key = "lb_..." (permanent — store this)

// From now on, use the permanent key
const permanentClient = new ListBee({ apiKey: key.key });

Resources

Resource Methods
listings create, get, list, update, publish, setDeliverables, removeDeliverables, addDeliverable, removeDeliverable, createComplete, delete
orders get, list, deliver, ship, refund
customers get, list
files upload
webhooks create, get, list, update, delete, listEvents, retryEvent, test, verify
account get, update, delete
apiKeys list, create, delete
signup sendOtp, verify
stripe connect, disconnect
utility ping

Listings

// Create
const listing = await client.listings.create({
  name: 'SEO Playbook 2026',
  price: 2900,
  description: 'A comprehensive guide to modern SEO techniques.',
  tagline: 'Updated for 2026 algorithm changes',
  highlights: ['50+ pages', 'Actionable tips', 'Free updates'],
  cta: 'Get Instant Access',
  cover_url: 'https://example.com/cover.png',
  compare_at_price: 3900,
  metadata: { source: 'n8n', campaign: 'launch-week' },
});
console.log(listing.id); // lst_r7kq2xy9m3pR5tW1

// Set deliverables (file, URL, or text)
import { Deliverable } from 'listbee';

await client.listings.setDeliverables(listing.id, {
  deliverables: [
    Deliverable.url('https://example.com/seo-playbook.pdf'),
  ],
});

// Publish
const published = await client.listings.publish(listing.id);
console.log(published.url); // https://buy.listbee.so/r7kq2xy9

// Get by ID
const listing = await client.listings.get('lst_r7kq2xy9m3pR5tW1');

// List — with filters
const page = await client.listings.list({
  status: 'published',
  limit: 20,
});
for (const l of page.data) {
  console.log(l.id, l.name, l.status);
}
console.log(page.total_count); // total matching listings

// Update
await client.listings.update('lst_r7kq2xy9m3pR5tW1', { price: 3900 });

// Remove deliverables (revert to external fulfillment)
await client.listings.removeDeliverables('lst_r7kq2xy9m3pR5tW1');

// Add a single deliverable using the Deliverable class
import { Deliverable } from 'listbee';

await client.listings.addDeliverable('lst_r7kq2xy9m3pR5tW1', Deliverable.url('https://example.com/playbook.pdf'));
await client.listings.addDeliverable('lst_r7kq2xy9m3pR5tW1', Deliverable.text('Your license key: XXXX-XXXX'));
await client.listings.addDeliverable('lst_r7kq2xy9m3pR5tW1', Deliverable.fromToken(file.token));

// Remove a single deliverable by del_ ID
await client.listings.removeDeliverable('lst_r7kq2xy9m3pR5tW1', 'del_4hR9nK2mQ7tV5wX1');

// Create a listing and attach deliverables in one call
const listing = await client.listings.createComplete({
  name: 'SEO Playbook',
  price: 2900,
  deliverables: [
    Deliverable.url('https://example.com/seo-playbook.pdf'),
  ],
});
await client.listings.publish(listing.id);

// Delete
await client.listings.delete('lst_r7kq2xy9m3pR5tW1');

Orders

// List — with filters
const page = await client.orders.list({
  status: 'paid',
  buyer_email: 'buyer@example.com',
  created_after: new Date('2026-03-01'),
  created_before: '2026-03-31T23:59:59Z',
});
console.log(page.total_count);

// Get by ID
const order = await client.orders.get('ord_9xM4kP7nR2qT5wY1');
console.log(order.status);       // "pending" | "paid" | "fulfilled" | "canceled" | "failed"
console.log(order.checkout_data); // custom checkout field values
console.log(order.paid_at);       // ISO 8601 timestamp

// Deliver — push content to the buyer (external fulfillment)
import { Deliverable } from 'listbee';

const fulfilled = await client.orders.deliver('ord_9xM4kP7nR2qT5wY1', {
  deliverables: [
    Deliverable.text('Your AI-generated report is ready.'),
  ],
});
console.log(fulfilled.status); // "fulfilled"

// Or deliver a URL
await client.orders.deliver('ord_9xM4kP7nR2qT5wY1', {
  deliverables: [
    Deliverable.url('https://example.com/report.pdf'),
  ],
});

// Ship — add tracking for physical orders
const shipped = await client.orders.ship('ord_9xM4kP7nR2qT5wY1', {
  carrier: 'USPS',
  tracking_code: '9400111899223',
  seller_note: 'Enjoy your purchase!',
});
console.log(shipped.status); // "fulfilled"

// Refund
const refunded = await client.orders.refund('ord_9xM4kP7nR2qT5wY1');
console.log(refunded.status); // "canceled"

Customers

// List customers
const page = await client.customers.list({ limit: 20, email: 'buyer@example.com' });
for (const c of page.data) {
  console.log(c.id, c.email);
}

// Get a customer
const customer = await client.customers.get('cus_7kQ2xY9mN3pR5tW1');
console.log(customer.email, customer.order_count);

Files

import { createReadStream } from 'fs';
import { Deliverable } from 'listbee';

// Upload a file (multipart, native FormData)
const file = await client.files.upload({
  file: createReadStream('/path/to/report.pdf'),
  filename: 'report.pdf',
});
console.log(file.token);  // use this token in setDeliverables / Deliverable.fromToken()

// Then attach to a listing
await client.listings.setDeliverables(listing.id, {
  deliverables: [
    Deliverable.fromToken(file.token),
  ],
});

Webhooks

import { ListBee, WebhookEventType } from 'listbee';

const client = new ListBee({ apiKey: 'lb_...' });

// Create — subscribe to specific events
const webhook = await client.webhooks.create({
  name: 'Production endpoint',
  url: 'https://example.com/webhooks/listbee',
  events: [
    WebhookEventType.ORDER_PAID,
    WebhookEventType.ORDER_FULFILLED,
    WebhookEventType.CUSTOMER_CREATED,
  ],
});
console.log(webhook.id);     // wh_3mK8nP2qR5tW7xY1
console.log(webhook.secret); // HMAC signing key — save this

// Verify an incoming webhook (HMAC-SHA256, uses crypto.subtle — no dependencies)
const isValid = await client.webhooks.verify({
  payload: rawBodyString,
  signature: req.headers['listbee-signature'],
  secret: webhook.secret,
});

// List delivery events
const events = await client.webhooks.listEvents('wh_3mK8nP2qR5tW7xY1', { status: 'failed' });

// Retry a failed event
await client.webhooks.retryEvent('wh_3mK8nP2qR5tW7xY1', 'evt_abc123');

// Update, test, delete
await client.webhooks.update('wh_3mK8nP2qR5tW7xY1', { enabled: false });
await client.webhooks.test('wh_3mK8nP2qR5tW7xY1');
await client.webhooks.delete('wh_3mK8nP2qR5tW7xY1');

Account

const account = await client.account.get();
console.log(account.id);    // acc_7kQ2xY9mN3pR5tW1
console.log(account.email);
console.log(account.plan);  // free | growth | scale
console.log(account.readiness.operational);

await client.account.update({ ga_measurement_id: 'G-XXXXXXXXXX' });
await client.account.delete();

Signup

Agent self-service onboarding — no API key required. Three steps: send OTP, verify code, create a permanent API key.

const client = new ListBee(); // no API key needed

// Step 1 — send OTP to email (creates account on first use)
const otpResult = await client.signup.sendOtp({ email: 'seller@example.com' });
console.log(otpResult.expires_in); // 300 (seconds until code expires)

// Step 2 — verify the 6-digit code
const session = await client.signup.verify({ email: 'seller@example.com', code: '123456' });
console.log(session.access_token); // at_... (24-hour access token)
console.log(session.is_new);       // true if account was just created

// Step 3 — create a permanent API key using the access token
const authedClient = new ListBee({ apiKey: session.access_token });
const apiKey = await authedClient.apiKeys.create({ name: 'Production' });
console.log(apiKey.key); // lb_... (save this immediately — shown once)

API keys

const keys = await client.apiKeys.list();

const newKey = await client.apiKeys.create({ name: 'CI pipeline' });
console.log(newKey.key); // lb_... (save this immediately)

await client.apiKeys.delete('lbk_7kQ2xY9mN3pR5tW1');

Stripe

// Generate a Stripe Connect onboarding link
const connect = await client.stripe.connect();
console.log(connect.url); // redirect seller here

// Disconnect Stripe
await client.stripe.disconnect();

Fulfillment modes

  • Managed — ListBee delivers digital content automatically. Call setDeliverables() on the listing, then publish().
  • External — ListBee fires order.paid webhook, your app handles delivery. Call orders.deliver() to push content back.
import { Deliverable } from 'listbee';

// One-shot: create listing + attach deliverables
const listing = await client.listings.createComplete({
  name: 'SEO Playbook',
  price: 2900,
  deliverables: [
    Deliverable.url('https://example.com/seo-playbook.pdf'),
  ],
});
await client.listings.publish(listing.id);

// Add or remove individual deliverables after creation
await client.listings.addDeliverable(listing.id, Deliverable.text('Bonus: license key XXXX-XXXX'));
await client.listings.removeDeliverable(listing.id, 'del_4hR9nK2mQ7tV5wX1');

// External fulfillment — AI-generated content
// Use CheckoutField builder for type-safe schema construction
const external = await client.listings.create({
  name: 'Custom SEO Report',
  price: 4900,
  fulfillment: 'external',
  checkout_schema: [
    CheckoutField.text('website_url', { label: 'Your website URL', sortOrder: 0 }),
    CheckoutField.select('urgency', { label: 'How urgent?', options: ['Low', 'Medium', 'High'], required: false, sortOrder: 1 }),
  ],
});
await client.listings.publish(external.id);

// On order.paid webhook:
await client.orders.deliver(order.id, {
  deliverables: [
    Deliverable.text(generatedReport),
  ],
});

Readiness system

Every listing and account includes a readiness field.

const account = await client.account.get();
if (!account.readiness.operational) {
  for (const action of account.readiness.actions) {
    if (action.kind === 'api') {
      console.log(`API action: ${action.code} -> ${action.resolve.endpoint}`);
    } else {
      console.log(`Manual step: ${action.code} -> ${action.resolve.url}`);
    }
  }
}

Idempotency

Pass idempotencyKey to any mutating request. Same key within 24h returns the cached response:

await client.listings.create(
  { name: 'SEO Playbook', price: 2900 },
  { idempotencyKey: 'create-listing-campaign-2026' },
);

Pagination

const page = await client.listings.list({ limit: 10 });
console.log(page.data);        // ListingResponse[]
console.log(page.has_more);    // true if more pages exist
console.log(page.cursor);      // pass to next call
console.log(page.total_count); // total matching items

if (page.has_more) {
  const nextPage = await client.listings.list({ limit: 10, cursor: page.cursor });
}

Utility

Verify API connectivity and that your API key is valid:

const ping = await client.utility.ping();
console.log(ping.status); // "ok"

Error handling

ListBeeError
├── APIConnectionError      network error — request never reached the server
├── APITimeoutError         request timed out
└── APIStatusError          server returned 4xx/5xx
    ├── BadRequestError          400
    ├── AuthenticationError      401
    ├── ForbiddenError           403
    ├── NotFoundError            404
    ├── ConflictError            409
    ├── PayloadTooLargeError     413
    ├── ValidationError          422
    ├── RateLimitError           429
    └── InternalServerError      500+
import { NotFoundError, AuthenticationError, RateLimitError, ErrorCode } from 'listbee';

try {
  await client.listings.get('lst_does-not-exist');
} catch (e) {
  if (e instanceof NotFoundError) {
    console.log(e.status); // 404
    console.log(e.code);   // ErrorCode value
    console.log(e.detail); // human-readable explanation
  } else if (e instanceof RateLimitError) {
    console.log(`Rate limited. Resets at ${e.reset}`);
  }
}

All APIStatusError subclasses expose: status, code (ErrorCode), detail, title, type, param.

Configuration

const client = new ListBee({
  apiKey: 'lb_...',
  timeoutMs: 60_000,   // default: 30000
  maxRetries: 5,        // default: 3; retries on 429/500/502/503/504
  baseUrl: 'https://api.listbee.so', // override for testing
});

Types

All types are importable from listbee:

import {
  ListBee,
  CursorPage,
  ErrorCode,
  Deliverable,     // input class: .file() | .url() | .text() | .fromToken()
  CheckoutField,   // input builder: .text() | .select() | .address() | .date()
  PartialCreationError,  // thrown when listing is created but deliverable attachment fails

  // Response types
  type ListingResponse,
  type OrderResponse,
  type CustomerResponse,
  type FileResponse,
  type WebhookResponse,
  type AccountResponse,
  type ApiKeyResponse,
  type SignupResponse,
  type VerifyResponse,
  type WebhookEventResponse,
  type WebhookTestResponse,

  // Enums
  DeliverableType,   // "file" | "url" | "text"
  FulfillmentMode,   // "managed" | "external"
  ListingStatus,     // "draft" | "published"
  OrderStatus,       // "pending" | "paid" | "fulfilled" | "canceled" | "failed"
  WebhookEventType,
  ActionCode,
  ActionKind,

  // Errors
  ListBeeError,
  APIStatusError,
  APIConnectionError,
  APITimeoutError,
  BadRequestError,
  AuthenticationError,
  ForbiddenError,
  NotFoundError,
  ConflictError,
  PayloadTooLargeError,
  ValidationError,
  RateLimitError,
  InternalServerError,
} from 'listbee';

Requirements

  • Node.js >= 18

License

Apache-2.0. See LICENSE.

Contributing

Bug reports and feature requests welcome — open an issue on GitHub.