JSPM

  • Created
  • Published
  • Downloads 79
  • Score
    100M100P100Q33822F
  • License Apache-2.0

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

Package Exports

  • listbee
  • listbee/api-keys
  • listbee/errors
  • listbee/orders
  • listbee/signup
  • listbee/stripe
  • listbee/types
  • listbee/webhooks

Readme

listbee

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

Install

npm install listbee
pnpm add listbee

Quick start

import { ListBee } from 'listbee';

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

// Managed fulfillment — ListBee delivers a file automatically
const listing = await client.listings.create({
  name: 'SEO Playbook',
  price: 2900,
  content: 'https://example.com/seo-playbook.pdf',
});
console.log(listing.url); // https://buy.listbee.so/r7kq2xy9

// External fulfillment — you handle delivery via webhooks
const report = await client.listings.create({
  name: 'Custom SEO Report',
  price: 4900,
  fulfillment: 'external',
  checkout_schema: [
    { type: 'text', key: 'website_url', label: 'Your website URL', required: true },
  ],
});

Using an environment variable instead:

export LISTBEE_API_KEY="lb_..."
import { ListBee } from 'listbee';

const client = new ListBee(); // reads LISTBEE_API_KEY automatically

const listing = await client.listings.create({
  name: 'SEO Playbook',
  price: 2900,
  content: 'https://example.com/seo-playbook.pdf',
});
console.log(listing.url);

Authentication

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

import { ListBee } from 'listbee';

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

// Environment variable (LISTBEE_API_KEY)
const client = new ListBee();

// process.env lookup
const client = new ListBee({ apiKey: process.env.LISTBEE_API_KEY });

The key is validated lazily — the client constructs successfully even with a missing or invalid key. An AuthenticationError is raised only when you make the first API call.

API keys start with lb_. Get yours at listbee.so.

Resources

Listings

import { ListBee } from 'listbee';

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

// Create — managed fulfillment (ListBee delivers the file)
const listing = await client.listings.create({
  name: 'SEO Playbook',
  price: 2900,       // $29.00 in cents
  content: 'https://example.com/seo-playbook.pdf', // file URL, redirect URL, or plain text
});

// Create — external fulfillment (you deliver via webhooks)
const report = await client.listings.create({
  name: 'Custom SEO Report',
  price: 4900,
  fulfillment: 'external',
  checkout_schema: [
    { type: 'text', key: 'website_url', label: 'Your website URL', required: true },
    { type: 'select', key: 'priority', label: 'Priority', required: false, options: ['standard', 'rush'] },
  ],
});

// Create — all optional params
const listing = await client.listings.create({
  name: 'SEO Playbook 2026',
  price: 2900,
  content: 'https://example.com/seo-playbook.pdf',
  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',          // buy button text; defaults to "Buy Now"
  cover_url: 'https://example.com/cover.png',
  compare_at_price: 3900,             // strikethrough price
  badges: ['Limited time', 'Best seller'],
  cover_blur: 'auto',                 // "auto" | "true" | "false"
  rating: 4.8,
  rating_count: 1243,
  reviews: [
    { name: 'Clara D.', rating: 5.0, content: 'Excellent quality content.' },
  ],
  faqs: [
    { q: 'Is this for beginners?', a: 'Yes, completely beginner-friendly.' },
  ],
  metadata: { source: 'n8n', campaign: 'launch-week' },
});
console.log(listing.id);  // lst_r7kq2xy9m3pR5tW1
console.log(listing.url); // https://buy.listbee.so/m3pr5tw1

// Get by slug
const listing = await client.listings.get('m3pr5tw1');

// List — paginated
const page = await client.listings.list({ limit: 20 });
for (const listing of page.data) {
  console.log(listing.slug, listing.name);
}

// Update — partial updates
const updated = await client.listings.update('r7kq2xy9', {
  name: 'SEO Playbook 2026 Updated',
  price: 3900,
});

// Pause / Resume
await client.listings.pause('r7kq2xy9');
await client.listings.resume('r7kq2xy9');

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

Orders

// List all orders
const page = await client.orders.list();
for (const order of page.data) {
  console.log(order.id, order.status);
}

// Filter by status, listing, date range
const page = await client.orders.list({
  status: 'paid',
  listing: 'seo-playbook',
  created_after: new Date('2026-03-01'),
  created_before: '2026-03-31T23:59:59Z', // Date objects or ISO strings
});

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

// Fulfill an order — push content to the buyer (external fulfillment)
const fulfilled = await client.orders.fulfill('ord_9xM4kP7nR2qT5wY1', {
  content: 'Your custom report is ready.',
  content_type: 'text',
});
console.log(fulfilled.status);            // "fulfilled"
console.log(fulfilled.fulfillment_status); // "fulfilled"

// Fulfill with a URL
await client.orders.fulfill('ord_9xM4kP7nR2qT5wY1', {
  content_url: 'https://example.com/report.pdf',
});

// Ship an order — add tracking info
const shipped = await client.orders.ship('ord_9xM4kP7nR2qT5wY1', {
  carrier: 'UPS',
  tracking_code: '1Z999AA10123456784',
  seller_note: 'Ships within 2 business days',
});
console.log(shipped.fulfillment_status); // "shipped"
console.log(shipped.carrier);           // "UPS"

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.ORDER_SHIPPED,
  ],
});
console.log(webhook.id);     // wh_3mK8nP2qR5tW7xY1
console.log(webhook.secret); // HMAC signing key

// Create — receive all events (omit events param)
const webhook = await client.webhooks.create({
  name: 'Catch-all',
  url: 'https://example.com/webhooks/listbee-all',
});

// List
const webhooks = await client.webhooks.list();
for (const wh of webhooks) {
  console.log(wh.id, wh.name, wh.enabled);
}

// Update — disable without deleting
await client.webhooks.update('wh_3mK8nP2qR5tW7xY1', { enabled: false });

// Update — change URL and events
await client.webhooks.update('wh_3mK8nP2qR5tW7xY1', {
  url: 'https://example.com/webhooks/v2',
  events: [WebhookEventType.ORDER_PAID],
});

// List delivery events
const events = await client.webhooks.listEvents('wh_3mK8nP2qR5tW7xY1', {
  status: 'failed',
});
for (const evt of events.data) {
  console.log(evt.event_type, evt.status, evt.last_error);
}

// Test endpoint
const test = await client.webhooks.test('wh_3mK8nP2qR5tW7xY1');
console.log(test.success, test.status_code);

// Delete
await client.webhooks.delete('wh_3mK8nP2qR5tW7xY1');

Account

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

// Update Google Analytics tracking
await client.account.update({ ga_measurement_id: 'G-XXXXXXXXXX' });

// Clear GA tracking
await client.account.update({ ga_measurement_id: null });

Signup

Agent self-service onboarding — no API key required:

import { ListBee } from 'listbee';

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

// Request a signup code
await client.signup.create({ email: 'seller@example.com' });

// Verify the code — returns account + API key
const result = await client.signup.verify({
  email: 'seller@example.com',
  code: '123456',
});
console.log(result.api_key); // lb_... (one-time display)

API keys

// List all API keys
const keys = await client.apiKeys.list();
for (const key of keys) {
  console.log(key.id, key.name);
}

// Create a new key — the key value is only shown once
const newKey = await client.apiKeys.create({ name: 'CI pipeline' });
console.log(newKey.key); // lb_... (save this immediately)

// Delete a key
await client.apiKeys.delete('lbk_7kQ2xY9mN3pR5tW1');

Stores

// Create a store
const store = await client.stores.create({ handle: 'fitness-brand', name: 'Fitness Brand' });
console.log(store.id);     // str_7kQ2xY9mN3pR5tW1vB8a
console.log(store.handle); // fitness-brand

// List all stores
const stores = await client.stores.list();
for (const s of stores.data) {
  console.log(s.handle, s.display_name);
}

// Get a store
const store = await client.stores.get('str_7kQ2xY9mN3pR5tW1vB8a');

// Update a store
const updated = await client.stores.update('str_7kQ2xY9mN3pR5tW1vB8a', {
  name: 'Fitness Pro',
  bio: 'Premium fitness content',
  social_links: ['https://twitter.com/fitnesspro'],
});

// Delete a store
await client.stores.delete('str_7kQ2xY9mN3pR5tW1vB8a');

// Connect Stripe — returns a URL to redirect the user to
const session = await client.stores.connectStripe('str_7kQ2xY9mN3pR5tW1vB8a');
console.log(session.url); // redirect seller here

// Custom domains
const domain = await client.stores.setDomain('str_7kQ2xY9mN3pR5tW1vB8a', { domain: 'fitness.com' });
console.log(domain.cname_target); // buy.listbee.so — point your CNAME here

const verified = await client.stores.verifyDomain('str_7kQ2xY9mN3pR5tW1vB8a');
console.log(verified.status); // "verified" or "pending"

await client.stores.removeDomain('str_7kQ2xY9mN3pR5tW1vB8a');

// Create a listing in a specific store
const listing = await client.listings.create({
  name: 'SEO Playbook',
  price: 2900,
  content: 'https://example.com/seo-playbook.pdf',
  store_id: 'str_7kQ2xY9mN3pR5tW1vB8a',
});

Stripe

// Set your Stripe secret key
await client.stripe.setKey({ secretKey: 'sk_live_...' });

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

ListBee supports two fulfillment modes:

  • Managed — ListBee delivers digital content automatically (files, URLs, text). Set content when creating a listing.
  • External — ListBee fires order.paid webhook, your app handles delivery. Use orders.fulfill() to push content back, or orders.ship() for physical goods.
import { ListBee, FulfillmentMode } from 'listbee';

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

// Check fulfillment mode
const listing = await client.listings.get('my-listing');
if (listing.fulfillment === FulfillmentMode.EXTERNAL) {
  // Handle delivery yourself
}

// Fulfill externally — push generated content
await client.orders.fulfill('ord_...', {
  content: 'Your AI-generated report...',
  content_type: 'text',
});

// Or ship physical goods
await client.orders.ship('ord_...', {
  carrier: 'FedEx',
  tracking_code: '7489274892',
});

Readiness system

Every listing and account includes a readiness field that tells you whether it can currently accept payments.

  • listing.readiness.sellabletrue when buyers can complete a purchase
  • account.readiness.operationaltrue when the account can sell

When false, an actions array explains what's needed and how to resolve each item. The next field points to the highest-priority action (prefers kind: api).

import { ActionKind } from 'listbee';

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

Pagination

listings.list() and orders.list() return a CursorPage with cursor-based pagination.

// First page
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);   // cursor string for the next page

// Fetch next page
if (page.has_more) {
  const nextPage = await client.listings.list({ limit: 10, cursor: page.cursor });
  console.log(nextPage.data);
}

Error handling

ListBeeError
├── APIConnectionError   // network error — request never reached the server
├── APITimeoutError      // request timed out
└── APIStatusError       // server returned 4xx/5xx
    ├── AuthenticationError    // 401 — invalid or missing API key
    ├── NotFoundError          // 404 — resource not found
    ├── ConflictError          // 409 — resource conflict
    ├── ValidationError        // 422 — request validation failed
    ├── RateLimitError         // 429 — rate limit exceeded
    └── InternalServerError    // 500+ — server-side error

Catch specific errors:

import {
  ListBee,
  AuthenticationError,
  NotFoundError,
  RateLimitError,
  ValidationError,
  APIConnectionError,
  APITimeoutError,
  APIStatusError,
} from 'listbee';

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

try {
  const listing = await client.listings.get('does-not-exist');
} catch (e) {
  if (e instanceof NotFoundError) {
    console.log(e.status); // 404
    console.log(e.code);   // machine-readable code
    console.log(e.detail); // human-readable explanation
  } else if (e instanceof AuthenticationError) {
    console.log('Invalid API key');
  } else if (e instanceof RateLimitError) {
    console.log(`Rate limited. Resets at ${e.reset}, ${e.remaining}/${e.limit} remaining`);
  } else if (e instanceof ValidationError) {
    console.log(`Bad request — field: ${e.param}, detail: ${e.detail}`);
  } else if (e instanceof APIConnectionError) {
    console.log('Network error — check your connection');
  } else if (e instanceof APITimeoutError) {
    console.log('Request timed out');
  } else if (e instanceof APIStatusError) {
    console.log(`API error ${e.status}: ${e.detail}`);
  }
}

Error responses follow RFC 9457 (Problem Details). All APIStatusError subclasses expose:

Attribute Type Description
status number HTTP status code
code string Machine-readable error code
detail string Specific explanation
title string Short, stable error category label
type string URI identifying the error type
param string | null Request field that caused the error

RateLimitError additionally exposes limit, remaining, and reset (parsed from response headers).

Configuration

import { ListBee } from 'listbee';

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', // default; override for testing
});

listings.create() uses a default timeout of 120 seconds because cover image processing can take longer. Pass timeoutMs to override:

const listing = await client.listings.create({
  name: 'Quick listing',
  price: 999,
  content: 'https://example.com/file.pdf',
  timeoutMs: 30_000, // override the 120s default for this call
});

Types and enums

All types are importable directly from listbee:

import {
  // Client
  ListBee,
  CursorPage,

  // Response types
  type ListingResponse,
  type OrderResponse,
  type WebhookResponse,
  type AccountResponse,
  type ApiKeyResponse,
  type SignupResponse,
  type VerifyResponse,
  type StripeConnectSessionResponse,
  type StoreResponse,
  type StoreListResponse,
  type DomainResponse,
  type WebhookEventResponse,
  type WebhookTestResponse,
  type CheckoutField,
  type ShippingAddress,

  // Param types
  type FulfillOrderParams,
  type ShipOrderParams,

  // Enums (runtime values)
  DeliverableType,     // "file" | "url" | "text"
  FulfillmentMode,     // "managed" | "external"
  CheckoutFieldType,   // "address" | "text" | "select" | "date"
  OrderStatus,         // "pending" | "paid" | "fulfilled" | "canceled" | "failed"
  FulfillmentStatus,   // "pending" | "shipped" | "fulfilled"
  WebhookEventType,    // "order.paid" | "order.fulfilled" | "order.shipped" | ...
  ActionCode,          // "set_stripe_key" | "connect_stripe" | "configure_webhook" | ...
  ActionKind,          // "api" | "human"

  // Deprecated (use DeliverableType)
  ContentType,

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

Enums are as const objects — use them to avoid magic strings:

import { DeliverableType, WebhookEventType, FulfillmentMode } from 'listbee';

// Check deliverable type
if (listing.deliverable_type === DeliverableType.FILE) {
  console.log('Delivers a file');
}

// Check fulfillment mode
if (listing.fulfillment === FulfillmentMode.EXTERNAL) {
  console.log('External fulfillment — handle delivery yourself');
}

// Subscribe to specific events
const webhook = await client.webhooks.create({
  name: 'Orders only',
  url: 'https://example.com/hooks',
  events: [
    WebhookEventType.ORDER_PAID,
    WebhookEventType.ORDER_FULFILLED,
  ],
});

Tree-shaking

Import individual resources for smaller bundles:

import { WebhooksResource } from 'listbee/webhooks';
import { OrdersResource } from 'listbee/orders';

Requirements

  • Node.js >= 18

License

Apache-2.0. See LICENSE.