livepasses 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Livepasses
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,431 @@
1
+ # Livepasses Node.js/TypeScript SDK
2
+
3
+ Official Node.js SDK for the [Livepasses API](https://livepasses.com) - generate, manage, and redeem Apple Wallet and Google Wallet passes.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install livepasses
9
+ ```
10
+
11
+ Requires **Node.js 18+** (uses native `fetch`). Zero runtime dependencies.
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { Livepasses } from 'livepasses';
17
+
18
+ const client = new Livepasses('lp_api_key_...');
19
+
20
+ const result = await client.passes.generate({
21
+ templateId: 'your-template-id',
22
+ passes: [{
23
+ customer: { firstName: 'Jane', lastName: 'Doe', email: 'jane@example.com' },
24
+ businessData: { sectionInfo: 'A', rowInfo: '12', seatNumber: '5' },
25
+ }],
26
+ });
27
+
28
+ console.log(result.passes[0].platforms.apple.addToWalletUrl);
29
+ ```
30
+
31
+ ## Authentication
32
+
33
+ Pass your API key when creating the client. Get your key at [dashboard.livepasses.com/api-keys](https://dashboard.livepasses.com/api-keys).
34
+
35
+ ```typescript
36
+ const client = new Livepasses('lp_api_key_...', {
37
+ baseUrl: 'https://api.livepasses.com', // default
38
+ timeout: 30000, // 30s default
39
+ maxRetries: 3, // default
40
+ });
41
+ ```
42
+
43
+ ## Pass Generation
44
+
45
+ ### Single pass (synchronous)
46
+
47
+ ```typescript
48
+ const result = await client.passes.generate({
49
+ templateId: 'template-id',
50
+ businessContext: {
51
+ event: {
52
+ eventName: 'Summer Concert 2026',
53
+ eventDate: '2026-07-15T18:00:00Z',
54
+ },
55
+ },
56
+ passes: [{
57
+ customer: { firstName: 'Jane', lastName: 'Doe', email: 'jane@example.com' },
58
+ businessData: {
59
+ sectionInfo: 'VIP', rowInfo: 'A', seatNumber: '12',
60
+ ticketType: 'VIP', price: 150, currency: 'USD',
61
+ },
62
+ }],
63
+ options: { deliveryMethod: 'email' },
64
+ });
65
+ ```
66
+
67
+ ### Batch passes (auto-polling)
68
+
69
+ For multiple recipients, `generateAndWait` automatically polls until the batch completes:
70
+
71
+ ```typescript
72
+ const result = await client.passes.generateAndWait(
73
+ {
74
+ templateId: 'template-id',
75
+ passes: recipients.map(r => ({
76
+ customer: r,
77
+ businessData: { ticketType: 'General' },
78
+ })),
79
+ },
80
+ {
81
+ pollInterval: 2000, // 2s between polls (default)
82
+ onProgress: (status) => {
83
+ console.log(`${status.progressPercentage}% complete`);
84
+ },
85
+ },
86
+ );
87
+ ```
88
+
89
+ ### Batch status (manual polling)
90
+
91
+ If you need more control over polling, use `generate` + `getBatchStatus`:
92
+
93
+ ```typescript
94
+ const initial = await client.passes.generate({
95
+ templateId: 'template-id',
96
+ passes: recipients.map(r => ({
97
+ customer: r,
98
+ businessData: { ticketType: 'General' },
99
+ })),
100
+ });
101
+
102
+ if (initial.batchOperation) {
103
+ let status = await client.passes.getBatchStatus(initial.batchOperation.batchId);
104
+ while (!status.isCompleted) {
105
+ await new Promise(r => setTimeout(r, 2000));
106
+ status = await client.passes.getBatchStatus(initial.batchOperation.batchId);
107
+ console.log(`Progress: ${status.progressPercentage}%`);
108
+ }
109
+ console.log(`Completed: ${status.statistics.successful} successful, ${status.statistics.failed} failed`);
110
+ }
111
+ ```
112
+
113
+ ## Pass Lifecycle
114
+
115
+ ### Lookup
116
+
117
+ ```typescript
118
+ const pass = await client.passes.lookup({ passId: 'pass-id' });
119
+ // or by pass number
120
+ const pass = await client.passes.lookup({ passNumber: 'LP-001' });
121
+ ```
122
+
123
+ ### Validate
124
+
125
+ ```typescript
126
+ const validation = await client.passes.validate('pass-id');
127
+ if (validation.canBeRedeemed) {
128
+ // proceed with redemption
129
+ }
130
+ ```
131
+
132
+ ### Redeem
133
+
134
+ ```typescript
135
+ // Generic redemption
136
+ const result = await client.passes.redeem('pass-id');
137
+
138
+ // Event check-in
139
+ const result = await client.passes.checkIn('pass-id', {
140
+ location: { name: 'Gate 1', latitude: 40.71, longitude: -74.00 },
141
+ });
142
+
143
+ // Coupon redemption
144
+ const result = await client.passes.redeemCoupon('pass-id');
145
+ ```
146
+
147
+ ### Update a pass
148
+
149
+ Update business data or context on an existing pass:
150
+
151
+ ```typescript
152
+ await client.passes.update('pass-id', {
153
+ businessData: { currentPoints: 750, memberTier: 'Platinum' },
154
+ businessContext: {
155
+ loyalty: { programUpdate: 'Congratulations on reaching Platinum!' },
156
+ },
157
+ });
158
+ ```
159
+
160
+ ### Bulk update
161
+
162
+ Update multiple passes at once:
163
+
164
+ ```typescript
165
+ await client.passes.bulkUpdate({
166
+ passIds: ['pass-1', 'pass-2', 'pass-3'],
167
+ businessData: { memberTier: 'Gold' },
168
+ businessContext: {
169
+ loyalty: { seasonalMessage: 'Happy holidays from our team!' },
170
+ },
171
+ });
172
+ ```
173
+
174
+ ## Pass Types
175
+
176
+ ### Event Passes
177
+
178
+ ```typescript
179
+ await client.passes.generate({
180
+ templateId: 'event-template-id',
181
+ businessContext: { event: { eventName: 'Concert', eventDate: '2026-07-15T18:00:00Z' } },
182
+ passes: [{
183
+ customer: { firstName: 'Jane', lastName: 'Doe' },
184
+ businessData: { sectionInfo: 'A', rowInfo: '1', seatNumber: '5', gateInfo: 'North' },
185
+ }],
186
+ });
187
+ ```
188
+
189
+ ### Loyalty Cards
190
+
191
+ ```typescript
192
+ await client.passes.generate({
193
+ templateId: 'loyalty-template-id',
194
+ passes: [{
195
+ customer: { firstName: 'Jane', lastName: 'Doe', email: 'jane@example.com' },
196
+ businessData: { membershipNumber: 'MEM-001', currentPoints: 500, memberTier: 'Gold' },
197
+ }],
198
+ });
199
+
200
+ // Earn points
201
+ await client.passes.loyaltyTransact('pass-id', {
202
+ transactionType: 'earn',
203
+ points: 100,
204
+ description: 'Purchase reward',
205
+ });
206
+
207
+ // Spend points
208
+ await client.passes.loyaltyTransact('pass-id', {
209
+ transactionType: 'spend',
210
+ points: 50,
211
+ description: 'Reward redemption',
212
+ });
213
+ ```
214
+
215
+ ### Coupon Passes
216
+
217
+ ```typescript
218
+ await client.passes.generate({
219
+ templateId: 'coupon-template-id',
220
+ businessContext: { coupon: { campaignName: 'Summer Sale', specialMessage: '20% off!' } },
221
+ passes: [{
222
+ customer: { firstName: 'Jane', lastName: 'Doe' },
223
+ businessData: { promoCode: 'SUMMER20', maxUsageCount: 1 },
224
+ }],
225
+ });
226
+ ```
227
+
228
+ ## Templates
229
+
230
+ ### List and get
231
+
232
+ ```typescript
233
+ // List templates
234
+ const templates = await client.templates.list({ status: 'Active' });
235
+
236
+ // Get template details
237
+ const template = await client.templates.get('template-id');
238
+ ```
239
+
240
+ ### Create a template
241
+
242
+ ```typescript
243
+ const template = await client.templates.create({
244
+ name: 'VIP Event Pass',
245
+ description: 'Premium event ticket template',
246
+ businessFeatures: {
247
+ passType: 'event',
248
+ hasSeating: true,
249
+ supportedPlatforms: ['apple', 'google'],
250
+ },
251
+ });
252
+ ```
253
+
254
+ ### Update a template
255
+
256
+ ```typescript
257
+ const updated = await client.templates.update('template-id', {
258
+ name: 'VIP Event Pass v2',
259
+ description: 'Updated premium event ticket template',
260
+ });
261
+ ```
262
+
263
+ ### Activate / deactivate
264
+
265
+ ```typescript
266
+ await client.templates.activate('template-id');
267
+ await client.templates.deactivate('template-id');
268
+ ```
269
+
270
+ ## Webhooks
271
+
272
+ ```typescript
273
+ // Register a webhook
274
+ const webhook = await client.webhooks.create({
275
+ url: 'https://your-app.com/webhooks/livepasses',
276
+ events: ['pass.generated', 'pass.redeemed', 'batch.completed'],
277
+ });
278
+ console.log(webhook.secret); // use this to verify webhook signatures
279
+
280
+ // List webhooks
281
+ const webhooks = await client.webhooks.list();
282
+
283
+ // Remove a webhook
284
+ await client.webhooks.delete('webhook-id');
285
+ ```
286
+
287
+ ## Error Handling
288
+
289
+ All errors are typed for precise `catch` handling:
290
+
291
+ ```typescript
292
+ import {
293
+ Livepasses,
294
+ LivepassesError,
295
+ AuthenticationError,
296
+ ValidationError,
297
+ ForbiddenError,
298
+ NotFoundError,
299
+ RateLimitError,
300
+ QuotaExceededError,
301
+ BusinessRuleError,
302
+ ApiErrorCodes,
303
+ } from 'livepasses';
304
+
305
+ try {
306
+ await client.passes.generate({ ... });
307
+ } catch (err) {
308
+ if (err instanceof AuthenticationError) {
309
+ console.error('Invalid API key');
310
+ } else if (err instanceof ValidationError) {
311
+ console.error('Invalid input:', err.message, err.details);
312
+ } else if (err instanceof ForbiddenError) {
313
+ console.error('Insufficient permissions for this operation');
314
+ } else if (err instanceof RateLimitError) {
315
+ console.error(`Rate limited. Retry after ${err.retryAfter}s`);
316
+ } else if (err instanceof QuotaExceededError) {
317
+ console.error('Monthly pass quota exceeded — upgrade your plan');
318
+ } else if (err instanceof NotFoundError) {
319
+ console.error('Template or pass not found');
320
+ } else if (err instanceof BusinessRuleError) {
321
+ console.error('Business rule:', err.message);
322
+ } else if (err instanceof LivepassesError) {
323
+ // Catch-all for any other API error
324
+ console.error(`API error [${err.code}]: ${err.message} (HTTP ${err.status})`);
325
+ }
326
+ }
327
+ ```
328
+
329
+ ### Error codes
330
+
331
+ Use the `ApiErrorCodes` constant for programmatic error code comparisons:
332
+
333
+ ```typescript
334
+ import { LivepassesError, ApiErrorCodes } from 'livepasses';
335
+
336
+ try {
337
+ await client.passes.redeem('pass-id');
338
+ } catch (err) {
339
+ if (err instanceof LivepassesError) {
340
+ switch (err.code) {
341
+ case ApiErrorCodes.PASS_ALREADY_USED:
342
+ console.error('This pass has already been redeemed');
343
+ break;
344
+ case ApiErrorCodes.PASS_EXPIRED:
345
+ console.error('This pass has expired');
346
+ break;
347
+ case ApiErrorCodes.TEMPLATE_INACTIVE:
348
+ console.error('The template for this pass is inactive');
349
+ break;
350
+ default:
351
+ console.error(`Unhandled error: ${err.code}`);
352
+ }
353
+ }
354
+ }
355
+ ```
356
+
357
+ ### Exception hierarchy
358
+
359
+ | Error Class | HTTP Status | When |
360
+ |-------------|------------|------|
361
+ | `AuthenticationError` | 401 | Invalid, expired, or revoked API key |
362
+ | `ValidationError` | 400 | Request validation failed |
363
+ | `ForbiddenError` | 403 | Insufficient permissions |
364
+ | `NotFoundError` | 404 | Resource not found |
365
+ | `RateLimitError` | 429 | Rate limit exceeded |
366
+ | `QuotaExceededError` | 403 | API quota or subscription limit exceeded |
367
+ | `BusinessRuleError` | 422 | Business rule violation (pass expired, already used, etc.) |
368
+
369
+ ## Pagination
370
+
371
+ ### Manual pagination
372
+
373
+ ```typescript
374
+ const page1 = await client.passes.list({ page: 1, pageSize: 50 });
375
+ console.log(`Page 1 of ${page1.pagination.totalPages} (${page1.pagination.totalItems} total)`);
376
+
377
+ // Get next page
378
+ if (page1.pagination.currentPage < page1.pagination.totalPages) {
379
+ const page2 = await client.passes.list({ page: 2, pageSize: 50 });
380
+ }
381
+ ```
382
+
383
+ ### Auto-pagination
384
+
385
+ ```typescript
386
+ for await (const pass of client.passes.listAutoPaginate({ templateId: 'tpl-id' })) {
387
+ console.log(pass.id);
388
+ }
389
+ ```
390
+
391
+ ## Configuration
392
+
393
+ | Option | Default | Description |
394
+ |--------|---------|-------------|
395
+ | `baseUrl` | `https://api.livepasses.com` | API base URL |
396
+ | `timeout` | `30000` | Request timeout in ms |
397
+ | `maxRetries` | `3` | Max retries for failed requests (429, 5xx) |
398
+
399
+ ### Automatic retries
400
+
401
+ The SDK automatically retries on:
402
+ - **429 Too Many Requests** — honors `Retry-After` header
403
+ - **5xx Server Errors** — exponential backoff with jitter
404
+
405
+ ## TypeScript
406
+
407
+ The SDK is written in TypeScript and ships with full type declarations. All request params and response types are exported:
408
+
409
+ ```typescript
410
+ import type { GeneratePassesParams, PassGenerationResult, BatchStatusResult } from 'livepasses';
411
+ ```
412
+
413
+ ## Examples
414
+
415
+ See the [`examples/`](./examples/) directory for runnable scripts:
416
+
417
+ - **[generate-passes.ts](./examples/generate-passes.ts)** — End-to-end pass generation, lookup, validation, and check-in
418
+ - **[loyalty-workflow.ts](./examples/loyalty-workflow.ts)** — Loyalty card lifecycle: generate, earn points, spend points, update tier
419
+ - **[coupon-workflow.ts](./examples/coupon-workflow.ts)** — Coupon pass generation and redemption
420
+ - **[template-management.ts](./examples/template-management.ts)** — CRUD operations on pass templates
421
+ - **[webhook-setup.ts](./examples/webhook-setup.ts)** — Register, list, and manage webhooks
422
+
423
+ Run any example with:
424
+ ```bash
425
+ export LIVEPASSES_API_KEY="your-api-key"
426
+ npx tsx examples/generate-passes.ts
427
+ ```
428
+
429
+ ## License
430
+
431
+ MIT