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 listbeepnpm add listbeeQuick 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
contentwhen creating a listing. - External — ListBee fires
order.paidwebhook, your app handles delivery. Useorders.fulfill()to push content back, ororders.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.sellable—truewhen buyers can complete a purchaseaccount.readiness.operational—truewhen 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 errorCatch 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.