JSPM

  • Created
  • Published
  • Downloads 80
  • Score
    100M100P100Q33921F
  • 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.

Resources

Resource Methods
listings create, get, list, update, publish, setDeliverables, removeDeliverables, addDeliverable, removeDeliverable, createComplete, delete
orders get, list, fulfill, refund
customers get, list
files upload
webhooks create, get, list, update, delete, listEvents, retryEvent, test, verify
account get, update, delete
apiKeys list, create, delete
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

// Fulfill — close out the order (no content delivery)
await client.orders.fulfill('ord_9xM4kP7nR2qT5wY1');

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

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

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

// Or upload a file and deliver it in one call
const fileDeliverable = Deliverable.file(Buffer.from(pdfBytes), 'report.pdf');
await client.orders.fulfill('ord_9xM4kP7nR2qT5wY1', { deliverables: [fileDeliverable] });

// 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.notify_orders);   // true if order email notifications are enabled
console.log(account.readiness.operational);

await client.account.update({ ga_measurement_id: 'G-XXXXXXXXXX' });
await client.account.update({ notify_orders: false }); // disable order email notifications
await client.account.delete();

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.fulfill() to push content back, or call it with no deliverables for a close-out.
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 — push content or close out
await client.orders.fulfill(order.id, {
  deliverables: [
    Deliverable.text(generatedReport),
  ],
});
// Or close out with no content:
await client.orders.fulfill(order.id);

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 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.