listbee 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/README.md +187 -381
  2. package/dist/cjs/base-client.d.ts +9 -29
  3. package/dist/cjs/base-client.js +58 -14
  4. package/dist/cjs/base-client.js.map +1 -1
  5. package/dist/cjs/client.d.ts +17 -13
  6. package/dist/cjs/client.js +14 -11
  7. package/dist/cjs/client.js.map +1 -1
  8. package/dist/cjs/errors.d.ts +12 -0
  9. package/dist/cjs/errors.js +31 -1
  10. package/dist/cjs/errors.js.map +1 -1
  11. package/dist/cjs/generated/api-types.d.ts +4089 -0
  12. package/dist/cjs/generated/api-types.js +7 -0
  13. package/dist/cjs/generated/api-types.js.map +1 -0
  14. package/dist/cjs/generated/enums.d.ts +94 -0
  15. package/dist/cjs/generated/enums.js +87 -0
  16. package/dist/cjs/generated/enums.js.map +1 -0
  17. package/dist/cjs/generated/operations.d.ts +233 -0
  18. package/dist/cjs/generated/operations.js +39 -0
  19. package/dist/cjs/generated/operations.js.map +1 -0
  20. package/dist/cjs/generated/types.d.ts +68 -0
  21. package/dist/cjs/{types/order.js → generated/types.js} +1 -4
  22. package/dist/cjs/generated/types.js.map +1 -0
  23. package/dist/cjs/index.d.ts +7 -13
  24. package/dist/cjs/index.js +22 -14
  25. package/dist/cjs/index.js.map +1 -1
  26. package/dist/cjs/resources/account.d.ts +11 -6
  27. package/dist/cjs/resources/account.js +19 -8
  28. package/dist/cjs/resources/account.js.map +1 -1
  29. package/dist/cjs/resources/api-keys.d.ts +18 -4
  30. package/dist/cjs/resources/api-keys.js +17 -1
  31. package/dist/cjs/resources/api-keys.js.map +1 -1
  32. package/dist/cjs/resources/customers.d.ts +23 -0
  33. package/dist/cjs/resources/customers.js +44 -0
  34. package/dist/cjs/resources/customers.js.map +1 -0
  35. package/dist/cjs/resources/files.d.ts +27 -0
  36. package/dist/cjs/resources/files.js +37 -0
  37. package/dist/cjs/resources/files.js.map +1 -0
  38. package/dist/cjs/resources/index.d.ts +3 -1
  39. package/dist/cjs/resources/index.js +7 -3
  40. package/dist/cjs/resources/index.js.map +1 -1
  41. package/dist/cjs/resources/listings.d.ts +35 -35
  42. package/dist/cjs/resources/listings.js +51 -116
  43. package/dist/cjs/resources/listings.js.map +1 -1
  44. package/dist/cjs/resources/orders.d.ts +28 -16
  45. package/dist/cjs/resources/orders.js +43 -36
  46. package/dist/cjs/resources/orders.js.map +1 -1
  47. package/dist/cjs/resources/signup.d.ts +21 -9
  48. package/dist/cjs/resources/signup.js +20 -3
  49. package/dist/cjs/resources/signup.js.map +1 -1
  50. package/dist/cjs/resources/stripe.d.ts +18 -6
  51. package/dist/cjs/resources/stripe.js +18 -7
  52. package/dist/cjs/resources/stripe.js.map +1 -1
  53. package/dist/cjs/resources/webhooks.d.ts +59 -3
  54. package/dist/cjs/resources/webhooks.js +102 -21
  55. package/dist/cjs/resources/webhooks.js.map +1 -1
  56. package/dist/cjs/types/index.d.ts +4 -10
  57. package/dist/cjs/types/index.js +17 -9
  58. package/dist/cjs/types/index.js.map +1 -1
  59. package/dist/cjs/types/shared.d.ts +25 -147
  60. package/dist/cjs/types/shared.js +12 -60
  61. package/dist/cjs/types/shared.js.map +1 -1
  62. package/dist/esm/base-client.d.ts +9 -29
  63. package/dist/esm/base-client.js +56 -12
  64. package/dist/esm/base-client.js.map +1 -1
  65. package/dist/esm/client.d.ts +17 -13
  66. package/dist/esm/client.js +14 -11
  67. package/dist/esm/client.js.map +1 -1
  68. package/dist/esm/errors.d.ts +12 -0
  69. package/dist/esm/errors.js +27 -0
  70. package/dist/esm/errors.js.map +1 -1
  71. package/dist/esm/generated/api-types.d.ts +4089 -0
  72. package/dist/esm/generated/api-types.js +6 -0
  73. package/dist/esm/generated/api-types.js.map +1 -0
  74. package/dist/esm/generated/enums.d.ts +94 -0
  75. package/dist/esm/generated/enums.js +84 -0
  76. package/dist/esm/generated/enums.js.map +1 -0
  77. package/dist/esm/generated/operations.d.ts +233 -0
  78. package/dist/esm/generated/operations.js +36 -0
  79. package/dist/esm/generated/operations.js.map +1 -0
  80. package/dist/esm/generated/types.d.ts +68 -0
  81. package/dist/esm/generated/types.js +2 -0
  82. package/dist/esm/generated/types.js.map +1 -0
  83. package/dist/esm/index.d.ts +7 -13
  84. package/dist/esm/index.js +5 -4
  85. package/dist/esm/index.js.map +1 -1
  86. package/dist/esm/resources/account.d.ts +11 -6
  87. package/dist/esm/resources/account.js +19 -8
  88. package/dist/esm/resources/account.js.map +1 -1
  89. package/dist/esm/resources/api-keys.d.ts +18 -4
  90. package/dist/esm/resources/api-keys.js +17 -1
  91. package/dist/esm/resources/api-keys.js.map +1 -1
  92. package/dist/esm/resources/customers.d.ts +23 -0
  93. package/dist/esm/resources/customers.js +39 -0
  94. package/dist/esm/resources/customers.js.map +1 -0
  95. package/dist/esm/resources/files.d.ts +27 -0
  96. package/dist/esm/resources/files.js +33 -0
  97. package/dist/esm/resources/files.js.map +1 -0
  98. package/dist/esm/resources/index.d.ts +3 -1
  99. package/dist/esm/resources/index.js +3 -1
  100. package/dist/esm/resources/index.js.map +1 -1
  101. package/dist/esm/resources/listings.d.ts +35 -35
  102. package/dist/esm/resources/listings.js +50 -116
  103. package/dist/esm/resources/listings.js.map +1 -1
  104. package/dist/esm/resources/orders.d.ts +28 -16
  105. package/dist/esm/resources/orders.js +43 -36
  106. package/dist/esm/resources/orders.js.map +1 -1
  107. package/dist/esm/resources/signup.d.ts +21 -9
  108. package/dist/esm/resources/signup.js +20 -3
  109. package/dist/esm/resources/signup.js.map +1 -1
  110. package/dist/esm/resources/stripe.d.ts +18 -6
  111. package/dist/esm/resources/stripe.js +18 -7
  112. package/dist/esm/resources/stripe.js.map +1 -1
  113. package/dist/esm/resources/webhooks.d.ts +59 -3
  114. package/dist/esm/resources/webhooks.js +101 -21
  115. package/dist/esm/resources/webhooks.js.map +1 -1
  116. package/dist/esm/types/index.d.ts +4 -10
  117. package/dist/esm/types/index.js +2 -1
  118. package/dist/esm/types/index.js.map +1 -1
  119. package/dist/esm/types/shared.d.ts +25 -147
  120. package/dist/esm/types/shared.js +10 -59
  121. package/dist/esm/types/shared.js.map +1 -1
  122. package/package.json +4 -82
  123. package/dist/cjs/resources/stores.d.ts +0 -31
  124. package/dist/cjs/resources/stores.js +0 -67
  125. package/dist/cjs/resources/stores.js.map +0 -1
  126. package/dist/cjs/types/account.d.ts +0 -47
  127. package/dist/cjs/types/account.js +0 -6
  128. package/dist/cjs/types/account.js.map +0 -1
  129. package/dist/cjs/types/api-key.d.ts +0 -18
  130. package/dist/cjs/types/api-key.js +0 -6
  131. package/dist/cjs/types/api-key.js.map +0 -1
  132. package/dist/cjs/types/listing.d.ts +0 -187
  133. package/dist/cjs/types/listing.js +0 -6
  134. package/dist/cjs/types/listing.js.map +0 -1
  135. package/dist/cjs/types/order.d.ts +0 -75
  136. package/dist/cjs/types/order.js.map +0 -1
  137. package/dist/cjs/types/signup.d.ts +0 -24
  138. package/dist/cjs/types/signup.js +0 -6
  139. package/dist/cjs/types/signup.js.map +0 -1
  140. package/dist/cjs/types/store.d.ts +0 -70
  141. package/dist/cjs/types/store.js +0 -6
  142. package/dist/cjs/types/store.js.map +0 -1
  143. package/dist/cjs/types/stripe.d.ts +0 -12
  144. package/dist/cjs/types/stripe.js +0 -6
  145. package/dist/cjs/types/stripe.js.map +0 -1
  146. package/dist/cjs/types/webhook.d.ts +0 -88
  147. package/dist/cjs/types/webhook.js +0 -6
  148. package/dist/cjs/types/webhook.js.map +0 -1
  149. package/dist/esm/resources/stores.d.ts +0 -31
  150. package/dist/esm/resources/stores.js +0 -63
  151. package/dist/esm/resources/stores.js.map +0 -1
  152. package/dist/esm/types/account.d.ts +0 -47
  153. package/dist/esm/types/account.js +0 -5
  154. package/dist/esm/types/account.js.map +0 -1
  155. package/dist/esm/types/api-key.d.ts +0 -18
  156. package/dist/esm/types/api-key.js +0 -5
  157. package/dist/esm/types/api-key.js.map +0 -1
  158. package/dist/esm/types/listing.d.ts +0 -187
  159. package/dist/esm/types/listing.js +0 -5
  160. package/dist/esm/types/listing.js.map +0 -1
  161. package/dist/esm/types/order.d.ts +0 -75
  162. package/dist/esm/types/order.js +0 -5
  163. package/dist/esm/types/order.js.map +0 -1
  164. package/dist/esm/types/signup.d.ts +0 -24
  165. package/dist/esm/types/signup.js +0 -5
  166. package/dist/esm/types/signup.js.map +0 -1
  167. package/dist/esm/types/store.d.ts +0 -70
  168. package/dist/esm/types/store.js +0 -5
  169. package/dist/esm/types/store.js.map +0 -1
  170. package/dist/esm/types/stripe.d.ts +0 -12
  171. package/dist/esm/types/stripe.js +0 -5
  172. package/dist/esm/types/stripe.js.map +0 -1
  173. package/dist/esm/types/webhook.d.ts +0 -88
  174. package/dist/esm/types/webhook.js +0 -5
  175. package/dist/esm/types/webhook.js.map +0 -1
package/README.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  Official TypeScript SDK for the [ListBee API](https://listbee.so) — one API call to sell and deliver digital content.
4
4
 
5
+ - Zero runtime dependencies (native fetch, Node >= 18)
6
+ - Types generated from OpenAPI spec — zero drift
7
+ - Full error hierarchy (RFC 9457)
8
+ - Cursor-based pagination
9
+ - Retry with exponential backoff
10
+ - Idempotency support
11
+
5
12
  ## Install
6
13
 
7
14
  ```bash
@@ -19,42 +26,29 @@ import { ListBee } from 'listbee';
19
26
 
20
27
  const client = new ListBee({ apiKey: 'lb_...' });
21
28
 
22
- // Managed fulfillment ListBee delivers a file automatically
29
+ // Create a listing, set a deliverable, publish
23
30
  const listing = await client.listings.create({
24
31
  name: 'SEO Playbook',
25
- price: 2900,
26
- content: 'https://example.com/seo-playbook.pdf',
32
+ price: 2900, // $29.00 in cents
33
+ description: 'A comprehensive guide to modern SEO.',
27
34
  });
28
- console.log(listing.url); // https://buy.listbee.so/r7kq2xy9
29
35
 
30
- // External fulfillment — you handle delivery via webhooks
31
- const report = await client.listings.create({
32
- name: 'Custom SEO Report',
33
- price: 4900,
34
- fulfillment: 'external',
35
- checkout_schema: [
36
- { type: 'text', key: 'website_url', label: 'Your website URL', required: true },
37
- ],
36
+ await client.listings.setDeliverables(listing.id, {
37
+ url: 'https://example.com/seo-playbook.pdf',
38
38
  });
39
+
40
+ const published = await client.listings.publish(listing.id);
41
+ console.log(published.url); // https://buy.listbee.so/r7kq2xy9
39
42
  ```
40
43
 
41
- Using an environment variable instead:
44
+ Or read from the environment:
42
45
 
43
46
  ```bash
44
47
  export LISTBEE_API_KEY="lb_..."
45
48
  ```
46
49
 
47
50
  ```typescript
48
- import { ListBee } from 'listbee';
49
-
50
51
  const client = new ListBee(); // reads LISTBEE_API_KEY automatically
51
-
52
- const listing = await client.listings.create({
53
- name: 'SEO Playbook',
54
- price: 2900,
55
- content: 'https://example.com/seo-playbook.pdf',
56
- });
57
- console.log(listing.url);
58
52
  ```
59
53
 
60
54
  ## Authentication
@@ -62,145 +56,138 @@ console.log(listing.url);
62
56
  Pass your API key explicitly or via the `LISTBEE_API_KEY` environment variable.
63
57
 
64
58
  ```typescript
65
- import { ListBee } from 'listbee';
66
-
67
- // Explicit key
68
59
  const client = new ListBee({ apiKey: 'lb_...' });
69
-
70
- // Environment variable (LISTBEE_API_KEY)
71
- const client = new ListBee();
72
-
73
- // process.env lookup
74
- const client = new ListBee({ apiKey: process.env.LISTBEE_API_KEY });
60
+ const client = new ListBee(); // env var
75
61
  ```
76
62
 
77
- 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.
78
-
79
- API keys start with `lb_`. Get yours at [listbee.so](https://listbee.so).
63
+ 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 [listbee.so](https://listbee.so).
80
64
 
81
65
  ## Resources
82
66
 
67
+ | Resource | Methods |
68
+ |----------|---------|
69
+ | `listings` | `create`, `get`, `list`, `update`, `publish`, `setDeliverables`, `removeDeliverables`, `delete` |
70
+ | `orders` | `get`, `list`, `deliver`, `refund` |
71
+ | `customers` | `get`, `list` |
72
+ | `files` | `upload` |
73
+ | `webhooks` | `create`, `get`, `list`, `update`, `delete`, `listEvents`, `retryEvent`, `test`, `verify` |
74
+ | `account` | `get`, `update`, `delete` |
75
+ | `apiKeys` | `list`, `create`, `delete` |
76
+ | `signup` | `create`, `verify` |
77
+ | `stripe` | `connect`, `disconnect` |
78
+
83
79
  ### Listings
84
80
 
85
81
  ```typescript
86
- import { ListBee } from 'listbee';
87
-
88
- const client = new ListBee({ apiKey: 'lb_...' });
89
-
90
- // Create — managed fulfillment (ListBee delivers the file)
91
- const listing = await client.listings.create({
92
- name: 'SEO Playbook',
93
- price: 2900, // $29.00 in cents
94
- content: 'https://example.com/seo-playbook.pdf', // file URL, redirect URL, or plain text
95
- });
96
-
97
- // Create — external fulfillment (you deliver via webhooks)
98
- const report = await client.listings.create({
99
- name: 'Custom SEO Report',
100
- price: 4900,
101
- fulfillment: 'external',
102
- checkout_schema: [
103
- { type: 'text', key: 'website_url', label: 'Your website URL', required: true },
104
- { type: 'select', key: 'priority', label: 'Priority', required: false, options: ['standard', 'rush'] },
105
- ],
106
- });
107
-
108
- // Create — all optional params
82
+ // Create
109
83
  const listing = await client.listings.create({
110
84
  name: 'SEO Playbook 2026',
111
85
  price: 2900,
112
- content: 'https://example.com/seo-playbook.pdf',
113
86
  description: 'A comprehensive guide to modern SEO techniques.',
114
87
  tagline: 'Updated for 2026 algorithm changes',
115
88
  highlights: ['50+ pages', 'Actionable tips', 'Free updates'],
116
- cta: 'Get Instant Access', // buy button text; defaults to "Buy Now"
89
+ cta: 'Get Instant Access',
117
90
  cover_url: 'https://example.com/cover.png',
118
- compare_at_price: 3900, // strikethrough price
119
- badges: ['Limited time', 'Best seller'],
120
- cover_blur: 'auto', // "auto" | "true" | "false"
121
- rating: 4.8,
122
- rating_count: 1243,
123
- reviews: [
124
- { name: 'Clara D.', rating: 5.0, content: 'Excellent quality content.' },
125
- ],
126
- faqs: [
127
- { q: 'Is this for beginners?', a: 'Yes, completely beginner-friendly.' },
128
- ],
91
+ compare_at_price: 3900,
129
92
  metadata: { source: 'n8n', campaign: 'launch-week' },
130
93
  });
131
- console.log(listing.id); // lst_r7kq2xy9m3pR5tW1
132
- console.log(listing.url); // https://buy.listbee.so/m3pr5tw1
94
+ console.log(listing.id); // lst_r7kq2xy9m3pR5tW1
133
95
 
134
- // Get by slug
135
- const listing = await client.listings.get('m3pr5tw1');
96
+ // Set deliverables (file, URL, or text)
97
+ await client.listings.setDeliverables(listing.id, {
98
+ url: 'https://example.com/seo-playbook.pdf',
99
+ });
136
100
 
137
- // List — paginated
138
- const page = await client.listings.list({ limit: 20 });
139
- for (const listing of page.data) {
140
- console.log(listing.slug, listing.name);
141
- }
101
+ // Publish
102
+ const published = await client.listings.publish(listing.id);
103
+ console.log(published.url); // https://buy.listbee.so/r7kq2xy9
142
104
 
143
- // Update partial updates
144
- const updated = await client.listings.update('r7kq2xy9', {
145
- name: 'SEO Playbook 2026 Updated',
146
- price: 3900,
105
+ // Get by ID
106
+ const listing = await client.listings.get('lst_r7kq2xy9m3pR5tW1');
107
+
108
+ // List — with filters
109
+ const page = await client.listings.list({
110
+ status: 'published',
111
+ limit: 20,
147
112
  });
113
+ for (const l of page.data) {
114
+ console.log(l.id, l.name, l.status);
115
+ }
116
+ console.log(page.total_count); // total matching listings
148
117
 
149
- // Pause / Resume
150
- await client.listings.pause('r7kq2xy9');
151
- await client.listings.resume('r7kq2xy9');
118
+ // Update
119
+ await client.listings.update('lst_r7kq2xy9m3pR5tW1', { price: 3900 });
120
+
121
+ // Remove deliverables (revert to external fulfillment)
122
+ await client.listings.removeDeliverables('lst_r7kq2xy9m3pR5tW1');
152
123
 
153
124
  // Delete
154
- await client.listings.delete('m3pr5tw1');
125
+ await client.listings.delete('lst_r7kq2xy9m3pR5tW1');
155
126
  ```
156
127
 
157
128
  ### Orders
158
129
 
159
130
  ```typescript
160
- // List all orders
161
- const page = await client.orders.list();
162
- for (const order of page.data) {
163
- console.log(order.id, order.status);
164
- }
165
-
166
- // Filter by status, listing, date range
131
+ // List with filters
167
132
  const page = await client.orders.list({
168
133
  status: 'paid',
169
- listing: 'seo-playbook',
134
+ buyer_email: 'buyer@example.com',
170
135
  created_after: new Date('2026-03-01'),
171
- created_before: '2026-03-31T23:59:59Z', // Date objects or ISO strings
136
+ created_before: '2026-03-31T23:59:59Z',
172
137
  });
138
+ console.log(page.total_count);
173
139
 
174
140
  // Get by ID
175
141
  const order = await client.orders.get('ord_9xM4kP7nR2qT5wY1');
176
- console.log(order.listing_id, order.amount);
177
- console.log(order.status); // "pending" | "paid" | "fulfilled" | "canceled" | "failed"
178
- console.log(order.fulfillment_status); // "pending" | "shipped" | "fulfilled" | null
179
- console.log(order.checkout_data); // custom checkout field values
180
- console.log(order.shipping_address); // ShippingAddress | null
181
- console.log(order.paid_at); // ISO 8601 timestamp
182
-
183
- // Fulfill an order — push content to the buyer (external fulfillment)
184
- const fulfilled = await client.orders.fulfill('ord_9xM4kP7nR2qT5wY1', {
185
- content: 'Your custom report is ready.',
142
+ console.log(order.status); // "pending" | "paid" | "fulfilled" | "canceled" | "failed"
143
+ console.log(order.checkout_data); // custom checkout field values
144
+ console.log(order.paid_at); // ISO 8601 timestamp
145
+
146
+ // Deliver push content to the buyer (external fulfillment)
147
+ const fulfilled = await client.orders.deliver('ord_9xM4kP7nR2qT5wY1', {
148
+ content: 'Your AI-generated report is ready.',
186
149
  content_type: 'text',
187
150
  });
188
- console.log(fulfilled.status); // "fulfilled"
189
- console.log(fulfilled.fulfillment_status); // "fulfilled"
151
+ console.log(fulfilled.status); // "fulfilled"
190
152
 
191
- // Fulfill with a URL
192
- await client.orders.fulfill('ord_9xM4kP7nR2qT5wY1', {
153
+ // Or with a URL
154
+ await client.orders.deliver('ord_9xM4kP7nR2qT5wY1', {
193
155
  content_url: 'https://example.com/report.pdf',
194
156
  });
195
157
 
196
- // Ship an order — add tracking info
197
- const shipped = await client.orders.ship('ord_9xM4kP7nR2qT5wY1', {
198
- carrier: 'UPS',
199
- tracking_code: '1Z999AA10123456784',
200
- seller_note: 'Ships within 2 business days',
158
+ // Refund
159
+ const refunded = await client.orders.refund('ord_9xM4kP7nR2qT5wY1');
160
+ console.log(refunded.status); // "canceled"
161
+ ```
162
+
163
+ ### Customers
164
+
165
+ ```typescript
166
+ // List customers
167
+ const page = await client.customers.list({ limit: 20, email: 'buyer@example.com' });
168
+ for (const c of page.data) {
169
+ console.log(c.id, c.email);
170
+ }
171
+
172
+ // Get a customer
173
+ const customer = await client.customers.get('cus_7kQ2xY9mN3pR5tW1');
174
+ console.log(customer.email, customer.order_count);
175
+ ```
176
+
177
+ ### Files
178
+
179
+ ```typescript
180
+ import { createReadStream } from 'fs';
181
+
182
+ // Upload a file (multipart, native FormData)
183
+ const file = await client.files.upload({
184
+ file: createReadStream('/path/to/report.pdf'),
185
+ filename: 'report.pdf',
201
186
  });
202
- console.log(shipped.fulfillment_status); // "shipped"
203
- console.log(shipped.carrier); // "UPS"
187
+ console.log(file.id); // use this ID in setDeliverables
188
+
189
+ // Then attach to a listing
190
+ await client.listings.setDeliverables(listing.id, { file_id: file.id });
204
191
  ```
205
192
 
206
193
  ### Webhooks
@@ -217,46 +204,28 @@ const webhook = await client.webhooks.create({
217
204
  events: [
218
205
  WebhookEventType.ORDER_PAID,
219
206
  WebhookEventType.ORDER_FULFILLED,
220
- WebhookEventType.ORDER_SHIPPED,
207
+ WebhookEventType.CUSTOMER_CREATED,
221
208
  ],
222
209
  });
223
210
  console.log(webhook.id); // wh_3mK8nP2qR5tW7xY1
224
- console.log(webhook.secret); // HMAC signing key
211
+ console.log(webhook.secret); // HMAC signing key — save this
225
212
 
226
- // Create receive all events (omit events param)
227
- const webhook = await client.webhooks.create({
228
- name: 'Catch-all',
229
- url: 'https://example.com/webhooks/listbee-all',
230
- });
231
-
232
- // List
233
- const webhooks = await client.webhooks.list();
234
- for (const wh of webhooks) {
235
- console.log(wh.id, wh.name, wh.enabled);
236
- }
237
-
238
- // Update — disable without deleting
239
- await client.webhooks.update('wh_3mK8nP2qR5tW7xY1', { enabled: false });
240
-
241
- // Update — change URL and events
242
- await client.webhooks.update('wh_3mK8nP2qR5tW7xY1', {
243
- url: 'https://example.com/webhooks/v2',
244
- events: [WebhookEventType.ORDER_PAID],
213
+ // Verify an incoming webhook (HMAC-SHA256, uses crypto.subtle — no dependencies)
214
+ const isValid = await client.webhooks.verify({
215
+ payload: rawBodyString,
216
+ signature: req.headers['listbee-signature'],
217
+ secret: webhook.secret,
245
218
  });
246
219
 
247
220
  // List delivery events
248
- const events = await client.webhooks.listEvents('wh_3mK8nP2qR5tW7xY1', {
249
- status: 'failed',
250
- });
251
- for (const evt of events.data) {
252
- console.log(evt.event_type, evt.status, evt.last_error);
253
- }
221
+ const events = await client.webhooks.listEvents('wh_3mK8nP2qR5tW7xY1', { status: 'failed' });
254
222
 
255
- // Test endpoint
256
- const test = await client.webhooks.test('wh_3mK8nP2qR5tW7xY1');
257
- console.log(test.success, test.status_code);
223
+ // Retry a failed event
224
+ await client.webhooks.retryEvent('wh_3mK8nP2qR5tW7xY1', 'evt_abc123');
258
225
 
259
- // Delete
226
+ // Update, test, delete
227
+ await client.webhooks.update('wh_3mK8nP2qR5tW7xY1', { enabled: false });
228
+ await client.webhooks.test('wh_3mK8nP2qR5tW7xY1');
260
229
  await client.webhooks.delete('wh_3mK8nP2qR5tW7xY1');
261
230
  ```
262
231
 
@@ -265,15 +234,12 @@ await client.webhooks.delete('wh_3mK8nP2qR5tW7xY1');
265
234
  ```typescript
266
235
  const account = await client.account.get();
267
236
  console.log(account.id); // acc_7kQ2xY9mN3pR5tW1
268
- console.log(account.email); // seller@example.com
237
+ console.log(account.email);
269
238
  console.log(account.plan); // free | growth | scale
270
239
  console.log(account.readiness.operational);
271
240
 
272
- // Update Google Analytics tracking
273
241
  await client.account.update({ ga_measurement_id: 'G-XXXXXXXXXX' });
274
-
275
- // Clear GA tracking
276
- await client.account.update({ ga_measurement_id: null });
242
+ await client.account.delete();
277
243
  ```
278
244
 
279
245
  ### Signup
@@ -281,93 +247,28 @@ await client.account.update({ ga_measurement_id: null });
281
247
  Agent self-service onboarding — no API key required:
282
248
 
283
249
  ```typescript
284
- import { ListBee } from 'listbee';
285
-
286
250
  const client = new ListBee(); // no API key needed
287
251
 
288
- // Request a signup code
289
252
  await client.signup.create({ email: 'seller@example.com' });
290
253
 
291
- // Verify the code returns account + API key
292
- const result = await client.signup.verify({
293
- email: 'seller@example.com',
294
- code: '123456',
295
- });
254
+ const result = await client.signup.verify({ email: 'seller@example.com', code: '123456' });
296
255
  console.log(result.api_key); // lb_... (one-time display)
297
256
  ```
298
257
 
299
258
  ### API keys
300
259
 
301
260
  ```typescript
302
- // List all API keys
303
261
  const keys = await client.apiKeys.list();
304
- for (const key of keys) {
305
- console.log(key.id, key.name);
306
- }
307
262
 
308
- // Create a new key — the key value is only shown once
309
263
  const newKey = await client.apiKeys.create({ name: 'CI pipeline' });
310
264
  console.log(newKey.key); // lb_... (save this immediately)
311
265
 
312
- // Delete a key
313
266
  await client.apiKeys.delete('lbk_7kQ2xY9mN3pR5tW1');
314
267
  ```
315
268
 
316
- ### Stores
317
-
318
- ```typescript
319
- // Create a store
320
- const store = await client.stores.create({ handle: 'fitness-brand', name: 'Fitness Brand' });
321
- console.log(store.id); // str_7kQ2xY9mN3pR5tW1vB8a
322
- console.log(store.handle); // fitness-brand
323
-
324
- // List all stores
325
- const stores = await client.stores.list();
326
- for (const s of stores.data) {
327
- console.log(s.handle, s.display_name);
328
- }
329
-
330
- // Get a store
331
- const store = await client.stores.get('str_7kQ2xY9mN3pR5tW1vB8a');
332
-
333
- // Update a store
334
- const updated = await client.stores.update('str_7kQ2xY9mN3pR5tW1vB8a', {
335
- name: 'Fitness Pro',
336
- bio: 'Premium fitness content',
337
- social_links: ['https://twitter.com/fitnesspro'],
338
- });
339
-
340
- // Delete a store
341
- await client.stores.delete('str_7kQ2xY9mN3pR5tW1vB8a');
342
-
343
- // Connect Stripe — returns a URL to redirect the user to
344
- const session = await client.stores.connectStripe('str_7kQ2xY9mN3pR5tW1vB8a');
345
- console.log(session.url); // redirect seller here
346
-
347
- // Custom domains
348
- const domain = await client.stores.setDomain('str_7kQ2xY9mN3pR5tW1vB8a', { domain: 'fitness.com' });
349
- console.log(domain.cname_target); // buy.listbee.so — point your CNAME here
350
-
351
- const verified = await client.stores.verifyDomain('str_7kQ2xY9mN3pR5tW1vB8a');
352
- console.log(verified.status); // "verified" or "pending"
353
-
354
- await client.stores.removeDomain('str_7kQ2xY9mN3pR5tW1vB8a');
355
-
356
- // Create a listing in a specific store
357
- const listing = await client.listings.create({
358
- name: 'SEO Playbook',
359
- price: 2900,
360
- content: 'https://example.com/seo-playbook.pdf',
361
- store_id: 'str_7kQ2xY9mN3pR5tW1vB8a',
362
- });
363
- ```
364
-
365
269
  ### Stripe
366
270
 
367
271
  ```typescript
368
- // Set your Stripe secret key
369
- await client.stripe.setKey({ secretKey: 'sk_live_...' });
370
-
371
272
  // Generate a Stripe Connect onboarding link
372
273
  const connect = await client.stripe.connect();
373
274
  console.log(connect.url); // redirect seller here
@@ -378,75 +279,67 @@ await client.stripe.disconnect();
378
279
 
379
280
  ## Fulfillment modes
380
281
 
381
- ListBee supports two fulfillment modes:
382
-
383
- - **Managed** — ListBee delivers digital content automatically (files, URLs, text). Set `content` when creating a listing.
384
- - **External** — ListBee fires `order.paid` webhook, your app handles delivery. Use `orders.fulfill()` to push content back, or `orders.ship()` for physical goods.
282
+ - **Managed** — ListBee delivers digital content automatically. Call `setDeliverables()` on the listing, then `publish()`.
283
+ - **External** — ListBee fires `order.paid` webhook, your app handles delivery. Call `orders.deliver()` to push content back.
385
284
 
386
285
  ```typescript
387
- import { ListBee, FulfillmentMode } from 'listbee';
388
-
389
- const client = new ListBee({ apiKey: 'lb_...' });
390
-
391
- // Check fulfillment mode
392
- const listing = await client.listings.get('my-listing');
393
- if (listing.fulfillment === FulfillmentMode.EXTERNAL) {
394
- // Handle delivery yourself
395
- }
396
-
397
- // Fulfill externally — push generated content
398
- await client.orders.fulfill('ord_...', {
399
- content: 'Your AI-generated report...',
400
- content_type: 'text',
286
+ // External fulfillment AI-generated content
287
+ const listing = await client.listings.create({
288
+ name: 'Custom SEO Report',
289
+ price: 4900,
290
+ fulfillment: 'external',
291
+ checkout_schema: [
292
+ { type: 'text', key: 'website_url', label: 'Your website URL', required: true },
293
+ ],
401
294
  });
295
+ await client.listings.publish(listing.id);
402
296
 
403
- // Or ship physical goods
404
- await client.orders.ship('ord_...', {
405
- carrier: 'FedEx',
406
- tracking_code: '7489274892',
297
+ // On order.paid webhook:
298
+ await client.orders.deliver(order.id, {
299
+ content: generatedReport,
300
+ content_type: 'text',
407
301
  });
408
302
  ```
409
303
 
410
304
  ## Readiness system
411
305
 
412
- Every listing and account includes a `readiness` field that tells you whether it can currently accept payments.
413
-
414
- - `listing.readiness.sellable` — `true` when buyers can complete a purchase
415
- - `account.readiness.operational` — `true` when the account can sell
416
-
417
- 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`).
306
+ Every listing and account includes a `readiness` field.
418
307
 
419
308
  ```typescript
420
- import { ActionKind } from 'listbee';
421
-
422
309
  const account = await client.account.get();
423
310
  if (!account.readiness.operational) {
424
311
  for (const action of account.readiness.actions) {
425
- if (action.kind === ActionKind.API) {
312
+ if (action.kind === 'api') {
426
313
  console.log(`API action: ${action.code} -> ${action.resolve.endpoint}`);
427
314
  } else {
428
315
  console.log(`Manual step: ${action.code} -> ${action.resolve.url}`);
429
316
  }
430
317
  }
431
- console.log(`Next action: ${account.readiness.next}`);
432
318
  }
433
319
  ```
434
320
 
435
- ## Pagination
321
+ ## Idempotency
322
+
323
+ Pass `idempotencyKey` to any mutating request. Same key within 24h returns the cached response:
324
+
325
+ ```typescript
326
+ await client.listings.create(
327
+ { name: 'SEO Playbook', price: 2900 },
328
+ { idempotencyKey: 'create-listing-campaign-2026' },
329
+ );
330
+ ```
436
331
 
437
- `listings.list()` and `orders.list()` return a `CursorPage` with cursor-based pagination.
332
+ ## Pagination
438
333
 
439
334
  ```typescript
440
- // First page
441
335
  const page = await client.listings.list({ limit: 10 });
442
- console.log(page.data); // ListingResponse[]
443
- console.log(page.has_more); // true if more pages exist
444
- console.log(page.cursor); // cursor string for the next page
336
+ console.log(page.data); // ListingResponse[]
337
+ console.log(page.has_more); // true if more pages exist
338
+ console.log(page.cursor); // pass to next call
339
+ console.log(page.total_count); // total matching items
445
340
 
446
- // Fetch next page
447
341
  if (page.has_more) {
448
342
  const nextPage = await client.listings.list({ limit: 10, cursor: page.cursor });
449
- console.log(nextPage.data);
450
343
  }
451
344
  ```
452
345
 
@@ -454,186 +347,99 @@ if (page.has_more) {
454
347
 
455
348
  ```
456
349
  ListBeeError
457
- ├── APIConnectionError // network error — request never reached the server
458
- ├── APITimeoutError // request timed out
459
- └── APIStatusError // server returned 4xx/5xx
460
- ├── AuthenticationError // 401 — invalid or missing API key
461
- ├── NotFoundError // 404 — resource not found
462
- ├── ConflictError // 409 — resource conflict
463
- ├── ValidationError // 422 — request validation failed
464
- ├── RateLimitError // 429 — rate limit exceeded
465
- └── InternalServerError // 500+ — server-side error
350
+ ├── APIConnectionError network error — request never reached the server
351
+ ├── APITimeoutError request timed out
352
+ └── APIStatusError server returned 4xx/5xx
353
+ ├── BadRequestError 400
354
+ ├── AuthenticationError 401
355
+ ├── ForbiddenError 403
356
+ ├── NotFoundError 404
357
+ ├── ConflictError 409
358
+ ├── PayloadTooLargeError 413
359
+ ├── ValidationError 422
360
+ ├── RateLimitError 429
361
+ └── InternalServerError 500+
466
362
  ```
467
363
 
468
- Catch specific errors:
469
-
470
364
  ```typescript
471
- import {
472
- ListBee,
473
- AuthenticationError,
474
- NotFoundError,
475
- RateLimitError,
476
- ValidationError,
477
- APIConnectionError,
478
- APITimeoutError,
479
- APIStatusError,
480
- } from 'listbee';
481
-
482
- const client = new ListBee({ apiKey: 'lb_...' });
365
+ import { NotFoundError, AuthenticationError, RateLimitError, ErrorCode } from 'listbee';
483
366
 
484
367
  try {
485
- const listing = await client.listings.get('does-not-exist');
368
+ await client.listings.get('lst_does-not-exist');
486
369
  } catch (e) {
487
370
  if (e instanceof NotFoundError) {
488
371
  console.log(e.status); // 404
489
- console.log(e.code); // machine-readable code
372
+ console.log(e.code); // ErrorCode value
490
373
  console.log(e.detail); // human-readable explanation
491
- } else if (e instanceof AuthenticationError) {
492
- console.log('Invalid API key');
493
374
  } else if (e instanceof RateLimitError) {
494
- console.log(`Rate limited. Resets at ${e.reset}, ${e.remaining}/${e.limit} remaining`);
495
- } else if (e instanceof ValidationError) {
496
- console.log(`Bad request — field: ${e.param}, detail: ${e.detail}`);
497
- } else if (e instanceof APIConnectionError) {
498
- console.log('Network error — check your connection');
499
- } else if (e instanceof APITimeoutError) {
500
- console.log('Request timed out');
501
- } else if (e instanceof APIStatusError) {
502
- console.log(`API error ${e.status}: ${e.detail}`);
375
+ console.log(`Rate limited. Resets at ${e.reset}`);
503
376
  }
504
377
  }
505
378
  ```
506
379
 
507
- Error responses follow [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) (Problem Details). All `APIStatusError` subclasses expose:
508
-
509
- | Attribute | Type | Description |
510
- |-----------|------|-------------|
511
- | `status` | `number` | HTTP status code |
512
- | `code` | `string` | Machine-readable error code |
513
- | `detail` | `string` | Specific explanation |
514
- | `title` | `string` | Short, stable error category label |
515
- | `type` | `string` | URI identifying the error type |
516
- | `param` | `string \| null` | Request field that caused the error |
517
-
518
- `RateLimitError` additionally exposes `limit`, `remaining`, and `reset` (parsed from response headers).
380
+ All `APIStatusError` subclasses expose: `status`, `code` (`ErrorCode`), `detail`, `title`, `type`, `param`.
519
381
 
520
382
  ## Configuration
521
383
 
522
384
  ```typescript
523
- import { ListBee } from 'listbee';
524
-
525
385
  const client = new ListBee({
526
386
  apiKey: 'lb_...',
527
- timeoutMs: 60_000, // default: 30000
387
+ timeoutMs: 60_000, // default: 30000
528
388
  maxRetries: 5, // default: 3; retries on 429/500/502/503/504
529
- baseUrl: 'https://api.listbee.so', // default; override for testing
530
- });
531
- ```
532
-
533
- `listings.create()` uses a default timeout of **120 seconds** because cover image processing can take longer. Pass `timeoutMs` to override:
534
-
535
- ```typescript
536
- const listing = await client.listings.create({
537
- name: 'Quick listing',
538
- price: 999,
539
- content: 'https://example.com/file.pdf',
540
- timeoutMs: 30_000, // override the 120s default for this call
389
+ baseUrl: 'https://api.listbee.so', // override for testing
541
390
  });
542
391
  ```
543
392
 
544
- ## Types and enums
393
+ ## Types
545
394
 
546
- All types are importable directly from `listbee`:
395
+ All types are importable from `listbee`:
547
396
 
548
397
  ```typescript
549
398
  import {
550
- // Client
551
399
  ListBee,
552
400
  CursorPage,
401
+ ErrorCode,
553
402
 
554
403
  // Response types
555
404
  type ListingResponse,
556
405
  type OrderResponse,
406
+ type CustomerResponse,
407
+ type FileResponse,
557
408
  type WebhookResponse,
558
409
  type AccountResponse,
559
410
  type ApiKeyResponse,
560
411
  type SignupResponse,
561
412
  type VerifyResponse,
562
- type StripeConnectSessionResponse,
563
- type StoreResponse,
564
- type StoreListResponse,
565
- type DomainResponse,
566
413
  type WebhookEventResponse,
567
414
  type WebhookTestResponse,
568
415
  type CheckoutField,
569
- type ShippingAddress,
570
-
571
- // Param types
572
- type FulfillOrderParams,
573
- type ShipOrderParams,
574
-
575
- // Enums (runtime values)
576
- DeliverableType, // "file" | "url" | "text"
577
- FulfillmentMode, // "managed" | "external"
578
- CheckoutFieldType, // "address" | "text" | "select" | "date"
579
- OrderStatus, // "pending" | "paid" | "fulfilled" | "canceled" | "failed"
580
- FulfillmentStatus, // "pending" | "shipped" | "fulfilled"
581
- WebhookEventType, // "order.paid" | "order.fulfilled" | "order.shipped" | ...
582
- ActionCode, // "set_stripe_key" | "connect_stripe" | "configure_webhook" | ...
583
- ActionKind, // "api" | "human"
584
416
 
585
- // Deprecated (use DeliverableType)
586
- ContentType,
417
+ // Enums
418
+ DeliverableType, // "file" | "url" | "text"
419
+ FulfillmentMode, // "managed" | "external"
420
+ ListingStatus, // "draft" | "published"
421
+ OrderStatus, // "pending" | "paid" | "fulfilled" | "canceled" | "failed"
422
+ WebhookEventType,
423
+ ActionCode,
424
+ ActionKind,
587
425
 
588
426
  // Errors
589
427
  ListBeeError,
590
428
  APIStatusError,
591
429
  APIConnectionError,
592
430
  APITimeoutError,
431
+ BadRequestError,
593
432
  AuthenticationError,
433
+ ForbiddenError,
594
434
  NotFoundError,
595
435
  ConflictError,
436
+ PayloadTooLargeError,
596
437
  ValidationError,
597
438
  RateLimitError,
598
439
  InternalServerError,
599
440
  } from 'listbee';
600
441
  ```
601
442
 
602
- Enums are `as const` objects — use them to avoid magic strings:
603
-
604
- ```typescript
605
- import { DeliverableType, WebhookEventType, FulfillmentMode } from 'listbee';
606
-
607
- // Check deliverable type
608
- if (listing.deliverable_type === DeliverableType.FILE) {
609
- console.log('Delivers a file');
610
- }
611
-
612
- // Check fulfillment mode
613
- if (listing.fulfillment === FulfillmentMode.EXTERNAL) {
614
- console.log('External fulfillment — handle delivery yourself');
615
- }
616
-
617
- // Subscribe to specific events
618
- const webhook = await client.webhooks.create({
619
- name: 'Orders only',
620
- url: 'https://example.com/hooks',
621
- events: [
622
- WebhookEventType.ORDER_PAID,
623
- WebhookEventType.ORDER_FULFILLED,
624
- ],
625
- });
626
- ```
627
-
628
- ## Tree-shaking
629
-
630
- Import individual resources for smaller bundles:
631
-
632
- ```typescript
633
- import { WebhooksResource } from 'listbee/webhooks';
634
- import { OrdersResource } from 'listbee/orders';
635
- ```
636
-
637
443
  ## Requirements
638
444
 
639
445
  - Node.js >= 18