@puul/partner-sdk 1.0.0 → 1.3.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/README.md CHANGED
@@ -139,8 +139,20 @@ await puul.wallet.deposit({
139
139
  currency: 'USDC',
140
140
  idempotency_key: 'dep_unique_123',
141
141
  });
142
+
143
+ // Withdraw from omnibus to configured bank/crypto destination
144
+ const withdrawal = await puul.wallet.withdraw({
145
+ amount: 500000, // $5,000 in minor units
146
+ currency: 'USDC',
147
+ method: 'bank', // or 'crypto'
148
+ });
149
+ // withdrawal.withdrawalId, withdrawal.status
142
150
  ```
143
151
 
152
+ > **Auto-Settlement:** If your `payout_method` is configured (via the admin dashboard),
153
+ > revenue share is automatically paid out after each market settlement. You don't need
154
+ > to call `withdraw()` manually — it happens at settlement time.
155
+
144
156
  ### Webhook Verification
145
157
 
146
158
  ```typescript
@@ -163,9 +175,16 @@ app.post('/webhooks/puul', express.raw({ type: '*/*' }), (req, res) => {
163
175
  case 'prediction.settled':
164
176
  console.log('Prediction settled:', event.data);
165
177
  break;
178
+ case 'prediction.voided':
179
+ console.log('Prediction voided:', event.data);
180
+ // Handle refund — user gets full stake back
181
+ break;
166
182
  case 'deposit.confirmed':
167
183
  console.log('Deposit confirmed:', event.data);
168
184
  break;
185
+ case 'withdrawal.completed':
186
+ console.log('Withdrawal completed:', event.data);
187
+ break;
169
188
  }
170
189
 
171
190
  res.status(200).send('OK');
package/dist/index.d.mts CHANGED
@@ -33,22 +33,27 @@ interface MarketOutcome {
33
33
  id: string;
34
34
  slug: string;
35
35
  label: string;
36
- pool: {
37
- pool_amount: string;
38
- total_bets: number;
39
- };
36
+ display_order: number;
37
+ pool_amount: string;
38
+ predictor_count: number;
39
+ prediction_count: number;
40
40
  }
41
41
  interface Market {
42
42
  id: string;
43
43
  question: string;
44
- description: string;
45
44
  category: string;
45
+ image_url: string;
46
+ market_type: string;
46
47
  status: string;
47
48
  currency: string;
48
49
  close_time: string;
49
50
  freeze_at: string | null;
50
- target_countries: string[];
51
51
  outcomes: MarketOutcome[];
52
+ volume: number;
53
+ volume_currency: string;
54
+ predictor_count: number;
55
+ prediction_count: number;
56
+ created_at: string;
52
57
  }
53
58
  type PredictionStatus = 'OPEN' | 'WON' | 'LOST' | 'VOIDED';
54
59
  type Currency = 'NGN' | 'USDC' | 'USDT' | 'KES' | 'GHS' | 'ZAR';
@@ -149,26 +154,238 @@ interface DepositParams {
149
154
  idempotency_key: string;
150
155
  reference?: string;
151
156
  }
152
- type WebhookEventType = 'prediction.settled' | 'deposit.confirmed' | 'withdrawal.completed';
153
- interface WebhookEvent<T = {
154
- [key: string]: unknown;
155
- }> {
157
+ type WithdrawMethod = 'bank' | 'crypto';
158
+ interface WithdrawParams {
159
+ /** Amount to withdraw in minor units (cents) */
160
+ amount: number;
161
+ /** Currency to withdraw. Defaults to USDC */
162
+ currency?: Currency;
163
+ /** Payout method: 'bank' for fiat, 'crypto' for on-chain */
164
+ method?: WithdrawMethod;
165
+ }
166
+ interface WithdrawResult {
167
+ withdrawalId: string;
168
+ partnerId: string;
169
+ amount: number;
170
+ currency: string;
171
+ method: WithdrawMethod;
172
+ payoutReference: string | null;
173
+ status: string;
174
+ }
175
+ type LotteryPoolType = 'winner_takes_all' | 'tiered';
176
+ type LotteryPoolFrequency = 'daily' | 'weekly' | 'monthly' | 'quarterly';
177
+ type LotteryPoolStatus = 'upcoming' | 'open' | 'drawing' | 'settled' | 'cancelled';
178
+ type LotteryEntryStatus = 'active' | 'won' | 'lost' | 'refunded';
179
+ interface PrizeTier {
180
+ /** 1-based rank (1 = first place) */
181
+ rank: number;
182
+ /** Percentage of distributable pool in basis points (5000 = 50%) */
183
+ pct: number;
184
+ }
185
+ interface LotteryPool {
186
+ id: string;
187
+ title: string;
188
+ description: string | null;
189
+ type: LotteryPoolType;
190
+ frequency: LotteryPoolFrequency;
191
+ status: LotteryPoolStatus;
192
+ ticket_price: string;
193
+ currency: string;
194
+ max_tickets: number | null;
195
+ max_tickets_per_user: number | null;
196
+ total_entries: number;
197
+ total_pool_amount: string;
198
+ opens_at: string;
199
+ draws_at: string;
200
+ drawn_at: string | null;
201
+ winning_numbers: number[];
202
+ prize_tiers: PrizeTier[];
203
+ takeout_rate_bps: number;
204
+ }
205
+ interface LotteryEntry {
206
+ id: string;
207
+ pool_id: string;
208
+ user_id: string | null;
209
+ user_external_id: string | null;
210
+ ticket_number: number;
211
+ amount_paid: string;
212
+ currency: string;
213
+ status: LotteryEntryStatus;
214
+ prize_amount: string | null;
215
+ prize_tier: number | null;
216
+ purchased_at: string;
217
+ }
218
+ interface LotteryEntriesResponse {
219
+ entries: LotteryEntry[];
220
+ total: number;
221
+ }
222
+ interface BuyTicketParams {
223
+ /** Puul user ID of the linked user */
224
+ userId: string;
225
+ /** Unique key to prevent duplicate purchases */
226
+ idempotencyKey: string;
227
+ /** Number of tickets (default: 1) */
228
+ quantity?: number;
229
+ /** Your external user ID (for tracking) */
230
+ userExternalId?: string;
231
+ }
232
+ interface BuyTicketResponse {
233
+ entries: LotteryEntry[];
234
+ }
235
+ interface LotteryDrawResult {
236
+ pool_id: string;
237
+ status: string;
238
+ drawn_at: string | null;
239
+ winning_numbers: number[];
240
+ total_pool_amount: string;
241
+ your_entries: Array<{
242
+ id: string;
243
+ ticket_number: number;
244
+ user_id: string | null;
245
+ user_external_id: string | null;
246
+ status: string;
247
+ prize_amount: string | null;
248
+ prize_tier: number | null;
249
+ }>;
250
+ your_winners: number;
251
+ your_total_winnings: number;
252
+ }
253
+ type WebhookEventType = 'user.created' | 'prediction.placed' | 'prediction.settled' | 'prediction.voided' | 'market.closed' | 'market.settled' | 'deposit.confirmed' | 'withdrawal.completed' | 'withdrawal.failed' | 'lottery.pool.drawn' | 'lottery.ticket.won';
254
+ interface WebhookEvent<T = unknown> {
156
255
  event: WebhookEventType;
157
256
  timestamp: string;
158
257
  data: T;
159
258
  }
160
- interface PredictionSettledData {
259
+ interface UserCreatedData {
260
+ puul_user_id: string;
261
+ external_user_id: string;
262
+ email: string;
263
+ phone: string;
264
+ country_code: string;
265
+ created_at: string;
266
+ }
267
+ interface PredictionPlacedData {
161
268
  prediction_id: string;
269
+ user_id: string;
270
+ user_external_id: string;
162
271
  market_id: string;
272
+ market_question: string;
163
273
  outcome_id: string;
164
274
  outcome_slug: string;
275
+ outcome_label: string;
276
+ stake_amount: number;
277
+ stake_currency: string;
278
+ estimated_return: string;
279
+ idempotency_key: string;
280
+ placed_at: string;
281
+ }
282
+ interface PredictionSettledData {
283
+ prediction_id: string;
284
+ user_id: string;
165
285
  user_external_id: string;
166
- status: 'WON' | 'LOST' | 'VOIDED';
286
+ market_id: string;
287
+ market_question: string;
288
+ outcome_id: string;
289
+ outcome_slug: string;
290
+ outcome_label: string;
291
+ stake_amount: string;
292
+ stake_currency: string;
293
+ status: 'WON' | 'LOST';
294
+ payout: string;
295
+ multiplier: string;
296
+ settled_at: string;
297
+ }
298
+ interface PredictionVoidedData {
299
+ prediction_id: string;
300
+ user_id: string;
301
+ user_external_id: string | null;
302
+ market_id: string;
303
+ market_question: string | null;
304
+ outcome_label: string | null;
167
305
  stake_amount: string;
168
306
  stake_currency: string;
169
- final_return: string;
307
+ refund_amount: string;
308
+ voided_at: string;
309
+ reason: string;
310
+ }
311
+ interface MarketClosedData {
312
+ market_id: string;
313
+ market_question: string;
314
+ market_type: string;
315
+ close_time: string;
316
+ closed_at: string;
317
+ currency: string;
318
+ volume: number;
319
+ prediction_count: number;
320
+ }
321
+ interface MarketSettledData {
322
+ market_id: string;
323
+ market_question: string;
324
+ market_type: string;
325
+ winning_outcome_id: string;
326
+ winning_outcome_slug: string | null;
327
+ winning_outcome_label: string | null;
328
+ currency: string;
329
+ total_pool: number;
330
+ winners_count: number;
331
+ total_payout: number;
170
332
  settled_at: string;
171
333
  }
334
+ interface DepositConfirmedData {
335
+ deposit_id: string;
336
+ user_id: string;
337
+ external_user_id: string;
338
+ amount: number;
339
+ currency: string;
340
+ amount_usd: number;
341
+ tx_hash: string;
342
+ status: string;
343
+ confirmed_at: string;
344
+ }
345
+ interface WithdrawalCompletedData {
346
+ withdrawal_id: string;
347
+ partner_id: string;
348
+ external_user_id: string;
349
+ amount: number;
350
+ currency: string;
351
+ method: string;
352
+ payout_reference: string | null;
353
+ completed_at: string;
354
+ }
355
+ interface WithdrawalFailedData {
356
+ withdrawal_id: string;
357
+ partner_id: string;
358
+ external_user_id: string;
359
+ amount: number;
360
+ currency: string;
361
+ method: string;
362
+ reason: string;
363
+ failed_at: string;
364
+ }
365
+ interface LotteryPoolDrawnData {
366
+ pool_id: string;
367
+ pool_title: string;
368
+ drawn_at: string;
369
+ winning_numbers: number[];
370
+ total_pool_amount: number;
371
+ total_entries: number;
372
+ takeout_amount: number;
373
+ total_payout: number;
374
+ your_entries: number;
375
+ your_winners: number;
376
+ your_total_winnings: number;
377
+ }
378
+ interface LotteryTicketWonData {
379
+ entry_id: string;
380
+ pool_id: string;
381
+ pool_title: string;
382
+ user_id: string;
383
+ user_external_id: string | null;
384
+ ticket_number: number;
385
+ prize_amount: number;
386
+ prize_tier: number;
387
+ drawn_at: string;
388
+ }
172
389
  interface PuulApiError {
173
390
  statusCode: number;
174
391
  message: string | string[];
@@ -220,6 +437,7 @@ declare class PuulPartner {
220
437
  readonly markets: MarketsAPI;
221
438
  readonly predictions: PredictionsAPI;
222
439
  readonly wallet: WalletAPI;
440
+ readonly lottery: LotteryAPI;
223
441
  constructor(config: PuulPartnerConfig);
224
442
  /** Get a valid access token, refreshing if needed */
225
443
  getAccessToken(): Promise<string>;
@@ -274,6 +492,55 @@ declare class WalletAPI {
274
492
  deposit(params: DepositParams): Promise<unknown>;
275
493
  /** Get withdrawal fee estimate */
276
494
  getWithdrawalFees(currency: string, amount: number): Promise<unknown>;
495
+ /**
496
+ * Withdraw from omnibus balance.
497
+ * Initiates a payout to the partner's configured bank account or crypto wallet.
498
+ *
499
+ * @example
500
+ * ```typescript
501
+ * const result = await puul.wallet.withdraw({
502
+ * amount: 500000, // $5,000 in cents
503
+ * currency: 'USDC',
504
+ * method: 'bank', // or 'crypto'
505
+ * });
506
+ * ```
507
+ */
508
+ withdraw(params: WithdrawParams): Promise<WithdrawResult>;
509
+ }
510
+ declare class LotteryAPI {
511
+ private readonly client;
512
+ constructor(client: PuulPartner);
513
+ /** List lottery pools, optionally filtered by status and frequency */
514
+ listPools(options?: {
515
+ status?: LotteryPoolStatus;
516
+ frequency?: string;
517
+ limit?: number;
518
+ }): Promise<LotteryPool[]>;
519
+ /** Get a specific pool by ID */
520
+ getPool(poolId: string): Promise<LotteryPool>;
521
+ /**
522
+ * Buy ticket(s) for a linked user.
523
+ *
524
+ * @example
525
+ * ```typescript
526
+ * const result = await puul.lottery.buyTicket('pool-uuid', {
527
+ * userId: 'puul-user-uuid',
528
+ * idempotencyKey: 'lottery-user456-pool1-001',
529
+ * quantity: 3,
530
+ * });
531
+ * ```
532
+ */
533
+ buyTicket(poolId: string, params: BuyTicketParams): Promise<BuyTicketResponse>;
534
+ /** List lottery entries for your partner account */
535
+ listEntries(options?: {
536
+ poolId?: string;
537
+ status?: LotteryEntryStatus;
538
+ limit?: number;
539
+ }): Promise<LotteryEntriesResponse>;
540
+ /** Get a single lottery entry by ID */
541
+ getEntry(entryId: string): Promise<LotteryEntry>;
542
+ /** Get draw results for a completed pool */
543
+ getDrawResults(poolId: string): Promise<LotteryDrawResult>;
277
544
  }
278
545
 
279
546
  /**
@@ -318,22 +585,51 @@ declare function verifyWebhookSignature(payload: string, signature: string, secr
318
585
  * const event = parseWebhookEvent(req.body);
319
586
  *
320
587
  * if (event.event === 'prediction.settled') {
321
- * const { prediction_id, status, final_return } = event.data;
588
+ * const { prediction_id, status, payout } = event.data;
322
589
  * // Handle settlement...
323
590
  * }
324
591
  * ```
325
592
  */
326
593
  declare function parseWebhookEvent(body: unknown): WebhookEvent;
327
- /**
328
- * Type guard for prediction.settled events.
329
- * Returns true if the event type is 'prediction.settled'.
330
- * Use this to narrow the type before accessing `.data` as PredictionSettledData.
331
- */
332
- declare function isPredictionSettledEvent(event: WebhookEvent): boolean;
594
+ /** Type guard for prediction.placed events. */
595
+ declare function isPredictionPlacedEvent(event: WebhookEvent): event is WebhookEvent<PredictionPlacedData>;
596
+ /** Type guard for prediction.settled events. */
597
+ declare function isPredictionSettledEvent(event: WebhookEvent): event is WebhookEvent<PredictionSettledData>;
598
+ /** Type guard for prediction.voided events. */
599
+ declare function isPredictionVoidedEvent(event: WebhookEvent): event is WebhookEvent<PredictionVoidedData>;
600
+ /** Type guard for market.closed events. */
601
+ declare function isMarketClosedEvent(event: WebhookEvent): event is WebhookEvent<MarketClosedData>;
602
+ /** Type guard for market.settled events. */
603
+ declare function isMarketSettledEvent(event: WebhookEvent): event is WebhookEvent<MarketSettledData>;
604
+ /** Type guard for deposit.confirmed events. */
605
+ declare function isDepositConfirmedEvent(event: WebhookEvent): event is WebhookEvent<DepositConfirmedData>;
606
+ /** Type guard for withdrawal.completed events. */
607
+ declare function isWithdrawalCompletedEvent(event: WebhookEvent): event is WebhookEvent<WithdrawalCompletedData>;
608
+ /** Type guard for withdrawal.failed events. */
609
+ declare function isWithdrawalFailedEvent(event: WebhookEvent): event is WebhookEvent<WithdrawalFailedData>;
610
+ /** Type guard for lottery.pool.drawn events. */
611
+ declare function isLotteryPoolDrawnEvent(event: WebhookEvent): event is WebhookEvent<LotteryPoolDrawnData>;
612
+ /** Type guard for lottery.ticket.won events. */
613
+ declare function isLotteryTicketWonEvent(event: WebhookEvent): event is WebhookEvent<LotteryTicketWonData>;
333
614
  /**
334
615
  * Convenience cast for prediction.settled event data.
335
616
  * Call after verifying with `isPredictionSettledEvent`.
336
617
  */
337
618
  declare function asPredictionSettledData(event: WebhookEvent): PredictionSettledData;
619
+ /**
620
+ * Convenience cast for market.settled event data.
621
+ * Call after verifying with `isMarketSettledEvent`.
622
+ */
623
+ declare function asMarketSettledData(event: WebhookEvent): MarketSettledData;
624
+ /**
625
+ * Convenience cast for lottery.pool.drawn event data.
626
+ * Call after verifying with `isLotteryPoolDrawnEvent`.
627
+ */
628
+ declare function asLotteryPoolDrawnData(event: WebhookEvent): LotteryPoolDrawnData;
629
+ /**
630
+ * Convenience cast for lottery.ticket.won event data.
631
+ * Call after verifying with `isLotteryTicketWonEvent`.
632
+ */
633
+ declare function asLotteryTicketWonData(event: WebhookEvent): LotteryTicketWonData;
338
634
 
339
- export { type AccessTokenResponse, type CreateLinkTokenParams, type CreatePendingPredictionParams, type CreateQuoteParams, type Currency, type DepositAccountInfo, type DepositParams, type LinkTokenResponse, type Market, type MarketOutcome, type PendingPrediction, type PlacePredictionParams, type Prediction, type PredictionListResponse, type PredictionSettledData, type PredictionStatus, type PuulApiError, PuulError, PuulPartner, type PuulPartnerConfig, type QuoteResponse, type SessionResponse, type WalletBalance, type WebhookEvent, type WebhookEventType, asPredictionSettledData, isPredictionSettledEvent, parseWebhookEvent, verifyWebhookSignature };
635
+ export { type AccessTokenResponse, type BuyTicketParams, type BuyTicketResponse, type CreateLinkTokenParams, type CreatePendingPredictionParams, type CreateQuoteParams, type Currency, type DepositAccountInfo, type DepositConfirmedData, type DepositParams, type LinkTokenResponse, type LotteryDrawResult, type LotteryEntriesResponse, type LotteryEntry, type LotteryEntryStatus, type LotteryPool, type LotteryPoolDrawnData, type LotteryPoolFrequency, type LotteryPoolStatus, type LotteryPoolType, type LotteryTicketWonData, type Market, type MarketClosedData, type MarketOutcome, type MarketSettledData, type PendingPrediction, type PlacePredictionParams, type Prediction, type PredictionListResponse, type PredictionPlacedData, type PredictionSettledData, type PredictionStatus, type PredictionVoidedData, type PrizeTier, type PuulApiError, PuulError, PuulPartner, type PuulPartnerConfig, type QuoteResponse, type SessionResponse, type UserCreatedData, type WalletBalance, type WebhookEvent, type WebhookEventType, type WithdrawMethod, type WithdrawParams, type WithdrawResult, type WithdrawalCompletedData, type WithdrawalFailedData, asLotteryPoolDrawnData, asLotteryTicketWonData, asMarketSettledData, asPredictionSettledData, isDepositConfirmedEvent, isLotteryPoolDrawnEvent, isLotteryTicketWonEvent, isMarketClosedEvent, isMarketSettledEvent, isPredictionPlacedEvent, isPredictionSettledEvent, isPredictionVoidedEvent, isWithdrawalCompletedEvent, isWithdrawalFailedEvent, parseWebhookEvent, verifyWebhookSignature };
package/dist/index.d.ts CHANGED
@@ -33,22 +33,27 @@ interface MarketOutcome {
33
33
  id: string;
34
34
  slug: string;
35
35
  label: string;
36
- pool: {
37
- pool_amount: string;
38
- total_bets: number;
39
- };
36
+ display_order: number;
37
+ pool_amount: string;
38
+ predictor_count: number;
39
+ prediction_count: number;
40
40
  }
41
41
  interface Market {
42
42
  id: string;
43
43
  question: string;
44
- description: string;
45
44
  category: string;
45
+ image_url: string;
46
+ market_type: string;
46
47
  status: string;
47
48
  currency: string;
48
49
  close_time: string;
49
50
  freeze_at: string | null;
50
- target_countries: string[];
51
51
  outcomes: MarketOutcome[];
52
+ volume: number;
53
+ volume_currency: string;
54
+ predictor_count: number;
55
+ prediction_count: number;
56
+ created_at: string;
52
57
  }
53
58
  type PredictionStatus = 'OPEN' | 'WON' | 'LOST' | 'VOIDED';
54
59
  type Currency = 'NGN' | 'USDC' | 'USDT' | 'KES' | 'GHS' | 'ZAR';
@@ -149,26 +154,238 @@ interface DepositParams {
149
154
  idempotency_key: string;
150
155
  reference?: string;
151
156
  }
152
- type WebhookEventType = 'prediction.settled' | 'deposit.confirmed' | 'withdrawal.completed';
153
- interface WebhookEvent<T = {
154
- [key: string]: unknown;
155
- }> {
157
+ type WithdrawMethod = 'bank' | 'crypto';
158
+ interface WithdrawParams {
159
+ /** Amount to withdraw in minor units (cents) */
160
+ amount: number;
161
+ /** Currency to withdraw. Defaults to USDC */
162
+ currency?: Currency;
163
+ /** Payout method: 'bank' for fiat, 'crypto' for on-chain */
164
+ method?: WithdrawMethod;
165
+ }
166
+ interface WithdrawResult {
167
+ withdrawalId: string;
168
+ partnerId: string;
169
+ amount: number;
170
+ currency: string;
171
+ method: WithdrawMethod;
172
+ payoutReference: string | null;
173
+ status: string;
174
+ }
175
+ type LotteryPoolType = 'winner_takes_all' | 'tiered';
176
+ type LotteryPoolFrequency = 'daily' | 'weekly' | 'monthly' | 'quarterly';
177
+ type LotteryPoolStatus = 'upcoming' | 'open' | 'drawing' | 'settled' | 'cancelled';
178
+ type LotteryEntryStatus = 'active' | 'won' | 'lost' | 'refunded';
179
+ interface PrizeTier {
180
+ /** 1-based rank (1 = first place) */
181
+ rank: number;
182
+ /** Percentage of distributable pool in basis points (5000 = 50%) */
183
+ pct: number;
184
+ }
185
+ interface LotteryPool {
186
+ id: string;
187
+ title: string;
188
+ description: string | null;
189
+ type: LotteryPoolType;
190
+ frequency: LotteryPoolFrequency;
191
+ status: LotteryPoolStatus;
192
+ ticket_price: string;
193
+ currency: string;
194
+ max_tickets: number | null;
195
+ max_tickets_per_user: number | null;
196
+ total_entries: number;
197
+ total_pool_amount: string;
198
+ opens_at: string;
199
+ draws_at: string;
200
+ drawn_at: string | null;
201
+ winning_numbers: number[];
202
+ prize_tiers: PrizeTier[];
203
+ takeout_rate_bps: number;
204
+ }
205
+ interface LotteryEntry {
206
+ id: string;
207
+ pool_id: string;
208
+ user_id: string | null;
209
+ user_external_id: string | null;
210
+ ticket_number: number;
211
+ amount_paid: string;
212
+ currency: string;
213
+ status: LotteryEntryStatus;
214
+ prize_amount: string | null;
215
+ prize_tier: number | null;
216
+ purchased_at: string;
217
+ }
218
+ interface LotteryEntriesResponse {
219
+ entries: LotteryEntry[];
220
+ total: number;
221
+ }
222
+ interface BuyTicketParams {
223
+ /** Puul user ID of the linked user */
224
+ userId: string;
225
+ /** Unique key to prevent duplicate purchases */
226
+ idempotencyKey: string;
227
+ /** Number of tickets (default: 1) */
228
+ quantity?: number;
229
+ /** Your external user ID (for tracking) */
230
+ userExternalId?: string;
231
+ }
232
+ interface BuyTicketResponse {
233
+ entries: LotteryEntry[];
234
+ }
235
+ interface LotteryDrawResult {
236
+ pool_id: string;
237
+ status: string;
238
+ drawn_at: string | null;
239
+ winning_numbers: number[];
240
+ total_pool_amount: string;
241
+ your_entries: Array<{
242
+ id: string;
243
+ ticket_number: number;
244
+ user_id: string | null;
245
+ user_external_id: string | null;
246
+ status: string;
247
+ prize_amount: string | null;
248
+ prize_tier: number | null;
249
+ }>;
250
+ your_winners: number;
251
+ your_total_winnings: number;
252
+ }
253
+ type WebhookEventType = 'user.created' | 'prediction.placed' | 'prediction.settled' | 'prediction.voided' | 'market.closed' | 'market.settled' | 'deposit.confirmed' | 'withdrawal.completed' | 'withdrawal.failed' | 'lottery.pool.drawn' | 'lottery.ticket.won';
254
+ interface WebhookEvent<T = unknown> {
156
255
  event: WebhookEventType;
157
256
  timestamp: string;
158
257
  data: T;
159
258
  }
160
- interface PredictionSettledData {
259
+ interface UserCreatedData {
260
+ puul_user_id: string;
261
+ external_user_id: string;
262
+ email: string;
263
+ phone: string;
264
+ country_code: string;
265
+ created_at: string;
266
+ }
267
+ interface PredictionPlacedData {
161
268
  prediction_id: string;
269
+ user_id: string;
270
+ user_external_id: string;
162
271
  market_id: string;
272
+ market_question: string;
163
273
  outcome_id: string;
164
274
  outcome_slug: string;
275
+ outcome_label: string;
276
+ stake_amount: number;
277
+ stake_currency: string;
278
+ estimated_return: string;
279
+ idempotency_key: string;
280
+ placed_at: string;
281
+ }
282
+ interface PredictionSettledData {
283
+ prediction_id: string;
284
+ user_id: string;
165
285
  user_external_id: string;
166
- status: 'WON' | 'LOST' | 'VOIDED';
286
+ market_id: string;
287
+ market_question: string;
288
+ outcome_id: string;
289
+ outcome_slug: string;
290
+ outcome_label: string;
291
+ stake_amount: string;
292
+ stake_currency: string;
293
+ status: 'WON' | 'LOST';
294
+ payout: string;
295
+ multiplier: string;
296
+ settled_at: string;
297
+ }
298
+ interface PredictionVoidedData {
299
+ prediction_id: string;
300
+ user_id: string;
301
+ user_external_id: string | null;
302
+ market_id: string;
303
+ market_question: string | null;
304
+ outcome_label: string | null;
167
305
  stake_amount: string;
168
306
  stake_currency: string;
169
- final_return: string;
307
+ refund_amount: string;
308
+ voided_at: string;
309
+ reason: string;
310
+ }
311
+ interface MarketClosedData {
312
+ market_id: string;
313
+ market_question: string;
314
+ market_type: string;
315
+ close_time: string;
316
+ closed_at: string;
317
+ currency: string;
318
+ volume: number;
319
+ prediction_count: number;
320
+ }
321
+ interface MarketSettledData {
322
+ market_id: string;
323
+ market_question: string;
324
+ market_type: string;
325
+ winning_outcome_id: string;
326
+ winning_outcome_slug: string | null;
327
+ winning_outcome_label: string | null;
328
+ currency: string;
329
+ total_pool: number;
330
+ winners_count: number;
331
+ total_payout: number;
170
332
  settled_at: string;
171
333
  }
334
+ interface DepositConfirmedData {
335
+ deposit_id: string;
336
+ user_id: string;
337
+ external_user_id: string;
338
+ amount: number;
339
+ currency: string;
340
+ amount_usd: number;
341
+ tx_hash: string;
342
+ status: string;
343
+ confirmed_at: string;
344
+ }
345
+ interface WithdrawalCompletedData {
346
+ withdrawal_id: string;
347
+ partner_id: string;
348
+ external_user_id: string;
349
+ amount: number;
350
+ currency: string;
351
+ method: string;
352
+ payout_reference: string | null;
353
+ completed_at: string;
354
+ }
355
+ interface WithdrawalFailedData {
356
+ withdrawal_id: string;
357
+ partner_id: string;
358
+ external_user_id: string;
359
+ amount: number;
360
+ currency: string;
361
+ method: string;
362
+ reason: string;
363
+ failed_at: string;
364
+ }
365
+ interface LotteryPoolDrawnData {
366
+ pool_id: string;
367
+ pool_title: string;
368
+ drawn_at: string;
369
+ winning_numbers: number[];
370
+ total_pool_amount: number;
371
+ total_entries: number;
372
+ takeout_amount: number;
373
+ total_payout: number;
374
+ your_entries: number;
375
+ your_winners: number;
376
+ your_total_winnings: number;
377
+ }
378
+ interface LotteryTicketWonData {
379
+ entry_id: string;
380
+ pool_id: string;
381
+ pool_title: string;
382
+ user_id: string;
383
+ user_external_id: string | null;
384
+ ticket_number: number;
385
+ prize_amount: number;
386
+ prize_tier: number;
387
+ drawn_at: string;
388
+ }
172
389
  interface PuulApiError {
173
390
  statusCode: number;
174
391
  message: string | string[];
@@ -220,6 +437,7 @@ declare class PuulPartner {
220
437
  readonly markets: MarketsAPI;
221
438
  readonly predictions: PredictionsAPI;
222
439
  readonly wallet: WalletAPI;
440
+ readonly lottery: LotteryAPI;
223
441
  constructor(config: PuulPartnerConfig);
224
442
  /** Get a valid access token, refreshing if needed */
225
443
  getAccessToken(): Promise<string>;
@@ -274,6 +492,55 @@ declare class WalletAPI {
274
492
  deposit(params: DepositParams): Promise<unknown>;
275
493
  /** Get withdrawal fee estimate */
276
494
  getWithdrawalFees(currency: string, amount: number): Promise<unknown>;
495
+ /**
496
+ * Withdraw from omnibus balance.
497
+ * Initiates a payout to the partner's configured bank account or crypto wallet.
498
+ *
499
+ * @example
500
+ * ```typescript
501
+ * const result = await puul.wallet.withdraw({
502
+ * amount: 500000, // $5,000 in cents
503
+ * currency: 'USDC',
504
+ * method: 'bank', // or 'crypto'
505
+ * });
506
+ * ```
507
+ */
508
+ withdraw(params: WithdrawParams): Promise<WithdrawResult>;
509
+ }
510
+ declare class LotteryAPI {
511
+ private readonly client;
512
+ constructor(client: PuulPartner);
513
+ /** List lottery pools, optionally filtered by status and frequency */
514
+ listPools(options?: {
515
+ status?: LotteryPoolStatus;
516
+ frequency?: string;
517
+ limit?: number;
518
+ }): Promise<LotteryPool[]>;
519
+ /** Get a specific pool by ID */
520
+ getPool(poolId: string): Promise<LotteryPool>;
521
+ /**
522
+ * Buy ticket(s) for a linked user.
523
+ *
524
+ * @example
525
+ * ```typescript
526
+ * const result = await puul.lottery.buyTicket('pool-uuid', {
527
+ * userId: 'puul-user-uuid',
528
+ * idempotencyKey: 'lottery-user456-pool1-001',
529
+ * quantity: 3,
530
+ * });
531
+ * ```
532
+ */
533
+ buyTicket(poolId: string, params: BuyTicketParams): Promise<BuyTicketResponse>;
534
+ /** List lottery entries for your partner account */
535
+ listEntries(options?: {
536
+ poolId?: string;
537
+ status?: LotteryEntryStatus;
538
+ limit?: number;
539
+ }): Promise<LotteryEntriesResponse>;
540
+ /** Get a single lottery entry by ID */
541
+ getEntry(entryId: string): Promise<LotteryEntry>;
542
+ /** Get draw results for a completed pool */
543
+ getDrawResults(poolId: string): Promise<LotteryDrawResult>;
277
544
  }
278
545
 
279
546
  /**
@@ -318,22 +585,51 @@ declare function verifyWebhookSignature(payload: string, signature: string, secr
318
585
  * const event = parseWebhookEvent(req.body);
319
586
  *
320
587
  * if (event.event === 'prediction.settled') {
321
- * const { prediction_id, status, final_return } = event.data;
588
+ * const { prediction_id, status, payout } = event.data;
322
589
  * // Handle settlement...
323
590
  * }
324
591
  * ```
325
592
  */
326
593
  declare function parseWebhookEvent(body: unknown): WebhookEvent;
327
- /**
328
- * Type guard for prediction.settled events.
329
- * Returns true if the event type is 'prediction.settled'.
330
- * Use this to narrow the type before accessing `.data` as PredictionSettledData.
331
- */
332
- declare function isPredictionSettledEvent(event: WebhookEvent): boolean;
594
+ /** Type guard for prediction.placed events. */
595
+ declare function isPredictionPlacedEvent(event: WebhookEvent): event is WebhookEvent<PredictionPlacedData>;
596
+ /** Type guard for prediction.settled events. */
597
+ declare function isPredictionSettledEvent(event: WebhookEvent): event is WebhookEvent<PredictionSettledData>;
598
+ /** Type guard for prediction.voided events. */
599
+ declare function isPredictionVoidedEvent(event: WebhookEvent): event is WebhookEvent<PredictionVoidedData>;
600
+ /** Type guard for market.closed events. */
601
+ declare function isMarketClosedEvent(event: WebhookEvent): event is WebhookEvent<MarketClosedData>;
602
+ /** Type guard for market.settled events. */
603
+ declare function isMarketSettledEvent(event: WebhookEvent): event is WebhookEvent<MarketSettledData>;
604
+ /** Type guard for deposit.confirmed events. */
605
+ declare function isDepositConfirmedEvent(event: WebhookEvent): event is WebhookEvent<DepositConfirmedData>;
606
+ /** Type guard for withdrawal.completed events. */
607
+ declare function isWithdrawalCompletedEvent(event: WebhookEvent): event is WebhookEvent<WithdrawalCompletedData>;
608
+ /** Type guard for withdrawal.failed events. */
609
+ declare function isWithdrawalFailedEvent(event: WebhookEvent): event is WebhookEvent<WithdrawalFailedData>;
610
+ /** Type guard for lottery.pool.drawn events. */
611
+ declare function isLotteryPoolDrawnEvent(event: WebhookEvent): event is WebhookEvent<LotteryPoolDrawnData>;
612
+ /** Type guard for lottery.ticket.won events. */
613
+ declare function isLotteryTicketWonEvent(event: WebhookEvent): event is WebhookEvent<LotteryTicketWonData>;
333
614
  /**
334
615
  * Convenience cast for prediction.settled event data.
335
616
  * Call after verifying with `isPredictionSettledEvent`.
336
617
  */
337
618
  declare function asPredictionSettledData(event: WebhookEvent): PredictionSettledData;
619
+ /**
620
+ * Convenience cast for market.settled event data.
621
+ * Call after verifying with `isMarketSettledEvent`.
622
+ */
623
+ declare function asMarketSettledData(event: WebhookEvent): MarketSettledData;
624
+ /**
625
+ * Convenience cast for lottery.pool.drawn event data.
626
+ * Call after verifying with `isLotteryPoolDrawnEvent`.
627
+ */
628
+ declare function asLotteryPoolDrawnData(event: WebhookEvent): LotteryPoolDrawnData;
629
+ /**
630
+ * Convenience cast for lottery.ticket.won event data.
631
+ * Call after verifying with `isLotteryTicketWonEvent`.
632
+ */
633
+ declare function asLotteryTicketWonData(event: WebhookEvent): LotteryTicketWonData;
338
634
 
339
- export { type AccessTokenResponse, type CreateLinkTokenParams, type CreatePendingPredictionParams, type CreateQuoteParams, type Currency, type DepositAccountInfo, type DepositParams, type LinkTokenResponse, type Market, type MarketOutcome, type PendingPrediction, type PlacePredictionParams, type Prediction, type PredictionListResponse, type PredictionSettledData, type PredictionStatus, type PuulApiError, PuulError, PuulPartner, type PuulPartnerConfig, type QuoteResponse, type SessionResponse, type WalletBalance, type WebhookEvent, type WebhookEventType, asPredictionSettledData, isPredictionSettledEvent, parseWebhookEvent, verifyWebhookSignature };
635
+ export { type AccessTokenResponse, type BuyTicketParams, type BuyTicketResponse, type CreateLinkTokenParams, type CreatePendingPredictionParams, type CreateQuoteParams, type Currency, type DepositAccountInfo, type DepositConfirmedData, type DepositParams, type LinkTokenResponse, type LotteryDrawResult, type LotteryEntriesResponse, type LotteryEntry, type LotteryEntryStatus, type LotteryPool, type LotteryPoolDrawnData, type LotteryPoolFrequency, type LotteryPoolStatus, type LotteryPoolType, type LotteryTicketWonData, type Market, type MarketClosedData, type MarketOutcome, type MarketSettledData, type PendingPrediction, type PlacePredictionParams, type Prediction, type PredictionListResponse, type PredictionPlacedData, type PredictionSettledData, type PredictionStatus, type PredictionVoidedData, type PrizeTier, type PuulApiError, PuulError, PuulPartner, type PuulPartnerConfig, type QuoteResponse, type SessionResponse, type UserCreatedData, type WalletBalance, type WebhookEvent, type WebhookEventType, type WithdrawMethod, type WithdrawParams, type WithdrawResult, type WithdrawalCompletedData, type WithdrawalFailedData, asLotteryPoolDrawnData, asLotteryTicketWonData, asMarketSettledData, asPredictionSettledData, isDepositConfirmedEvent, isLotteryPoolDrawnEvent, isLotteryTicketWonEvent, isMarketClosedEvent, isMarketSettledEvent, isPredictionPlacedEvent, isPredictionSettledEvent, isPredictionVoidedEvent, isWithdrawalCompletedEvent, isWithdrawalFailedEvent, parseWebhookEvent, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -22,8 +22,20 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  PuulError: () => PuulError,
24
24
  PuulPartner: () => PuulPartner,
25
+ asLotteryPoolDrawnData: () => asLotteryPoolDrawnData,
26
+ asLotteryTicketWonData: () => asLotteryTicketWonData,
27
+ asMarketSettledData: () => asMarketSettledData,
25
28
  asPredictionSettledData: () => asPredictionSettledData,
29
+ isDepositConfirmedEvent: () => isDepositConfirmedEvent,
30
+ isLotteryPoolDrawnEvent: () => isLotteryPoolDrawnEvent,
31
+ isLotteryTicketWonEvent: () => isLotteryTicketWonEvent,
32
+ isMarketClosedEvent: () => isMarketClosedEvent,
33
+ isMarketSettledEvent: () => isMarketSettledEvent,
34
+ isPredictionPlacedEvent: () => isPredictionPlacedEvent,
26
35
  isPredictionSettledEvent: () => isPredictionSettledEvent,
36
+ isPredictionVoidedEvent: () => isPredictionVoidedEvent,
37
+ isWithdrawalCompletedEvent: () => isWithdrawalCompletedEvent,
38
+ isWithdrawalFailedEvent: () => isWithdrawalFailedEvent,
27
39
  parseWebhookEvent: () => parseWebhookEvent,
28
40
  verifyWebhookSignature: () => verifyWebhookSignature
29
41
  });
@@ -59,6 +71,7 @@ var PuulPartner = class {
59
71
  this.markets = new MarketsAPI(this);
60
72
  this.predictions = new PredictionsAPI(this);
61
73
  this.wallet = new WalletAPI(this);
74
+ this.lottery = new LotteryAPI(this);
62
75
  }
63
76
  // ==========================================================
64
77
  // Internal HTTP layer
@@ -229,22 +242,93 @@ var WalletAPI = class {
229
242
  async getWithdrawalFees(currency, amount) {
230
243
  return this.client.request("GET", `/partner/wallet/withdrawal-fees?currency=${currency}&amount=${amount}`);
231
244
  }
245
+ /**
246
+ * Withdraw from omnibus balance.
247
+ * Initiates a payout to the partner's configured bank account or crypto wallet.
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * const result = await puul.wallet.withdraw({
252
+ * amount: 500000, // $5,000 in cents
253
+ * currency: 'USDC',
254
+ * method: 'bank', // or 'crypto'
255
+ * });
256
+ * ```
257
+ */
258
+ async withdraw(params) {
259
+ return this.client.request("POST", "/partner/wallet/withdraw", params);
260
+ }
261
+ };
262
+ var LotteryAPI = class {
263
+ constructor(client) {
264
+ this.client = client;
265
+ }
266
+ /** List lottery pools, optionally filtered by status and frequency */
267
+ async listPools(options) {
268
+ const params = new URLSearchParams();
269
+ if (options?.status) params.set("status", options.status);
270
+ if (options?.frequency) params.set("frequency", options.frequency);
271
+ if (options?.limit) params.set("limit", String(options.limit));
272
+ const query = params.toString() ? `?${params.toString()}` : "";
273
+ return this.client.request("GET", `/partner/lottery/pools${query}`);
274
+ }
275
+ /** Get a specific pool by ID */
276
+ async getPool(poolId) {
277
+ return this.client.request("GET", `/partner/lottery/pools/${poolId}`);
278
+ }
279
+ /**
280
+ * Buy ticket(s) for a linked user.
281
+ *
282
+ * @example
283
+ * ```typescript
284
+ * const result = await puul.lottery.buyTicket('pool-uuid', {
285
+ * userId: 'puul-user-uuid',
286
+ * idempotencyKey: 'lottery-user456-pool1-001',
287
+ * quantity: 3,
288
+ * });
289
+ * ```
290
+ */
291
+ async buyTicket(poolId, params) {
292
+ return this.client.request("POST", `/partner/lottery/pools/${poolId}/buy`, {
293
+ user_id: params.userId,
294
+ idempotency_key: params.idempotencyKey,
295
+ quantity: params.quantity,
296
+ user_external_id: params.userExternalId
297
+ });
298
+ }
299
+ /** List lottery entries for your partner account */
300
+ async listEntries(options) {
301
+ const params = new URLSearchParams();
302
+ if (options?.poolId) params.set("pool_id", options.poolId);
303
+ if (options?.status) params.set("status", options.status);
304
+ if (options?.limit) params.set("limit", String(options.limit));
305
+ const query = params.toString() ? `?${params.toString()}` : "";
306
+ return this.client.request("GET", `/partner/lottery/entries${query}`);
307
+ }
308
+ /** Get a single lottery entry by ID */
309
+ async getEntry(entryId) {
310
+ return this.client.request("GET", `/partner/lottery/entries/${entryId}`);
311
+ }
312
+ /** Get draw results for a completed pool */
313
+ async getDrawResults(poolId) {
314
+ return this.client.request("GET", `/partner/lottery/pools/${poolId}/results`);
315
+ }
232
316
  };
233
317
 
234
318
  // src/webhooks.ts
235
- var import_crypto = require("crypto");
319
+ var import_node_crypto = require("crypto");
236
320
  function verifyWebhookSignature(payload, signature, secret) {
237
321
  if (!payload || !signature || !secret) {
238
322
  return false;
239
323
  }
240
- const expectedSignature = (0, import_crypto.createHmac)("sha256", secret).update(payload).digest("hex");
324
+ const expectedSignature = (0, import_node_crypto.createHmac)("sha256", secret).update(payload).digest("hex");
241
325
  try {
242
326
  const sigBuffer = Buffer.from(signature, "hex");
243
327
  const expectedBuffer = Buffer.from(expectedSignature, "hex");
244
328
  if (sigBuffer.length !== expectedBuffer.length) {
245
329
  return false;
246
330
  }
247
- return (0, import_crypto.timingSafeEqual)(sigBuffer, expectedBuffer);
331
+ return (0, import_node_crypto.timingSafeEqual)(sigBuffer, expectedBuffer);
248
332
  } catch {
249
333
  return false;
250
334
  }
@@ -255,7 +339,7 @@ function parseWebhookEvent(body) {
255
339
  }
256
340
  const event = body;
257
341
  if (typeof event.event !== "string") {
258
- throw new Error('Invalid webhook body: missing "event" field');
342
+ throw new TypeError('Invalid webhook body: missing "event" field');
259
343
  }
260
344
  return {
261
345
  event: event.event,
@@ -263,18 +347,66 @@ function parseWebhookEvent(body) {
263
347
  data: event.data || {}
264
348
  };
265
349
  }
350
+ function isPredictionPlacedEvent(event) {
351
+ return event.event === "prediction.placed";
352
+ }
266
353
  function isPredictionSettledEvent(event) {
267
354
  return event.event === "prediction.settled";
268
355
  }
356
+ function isPredictionVoidedEvent(event) {
357
+ return event.event === "prediction.voided";
358
+ }
359
+ function isMarketClosedEvent(event) {
360
+ return event.event === "market.closed";
361
+ }
362
+ function isMarketSettledEvent(event) {
363
+ return event.event === "market.settled";
364
+ }
365
+ function isDepositConfirmedEvent(event) {
366
+ return event.event === "deposit.confirmed";
367
+ }
368
+ function isWithdrawalCompletedEvent(event) {
369
+ return event.event === "withdrawal.completed";
370
+ }
371
+ function isWithdrawalFailedEvent(event) {
372
+ return event.event === "withdrawal.failed";
373
+ }
374
+ function isLotteryPoolDrawnEvent(event) {
375
+ return event.event === "lottery.pool.drawn";
376
+ }
377
+ function isLotteryTicketWonEvent(event) {
378
+ return event.event === "lottery.ticket.won";
379
+ }
269
380
  function asPredictionSettledData(event) {
270
381
  return event.data;
271
382
  }
383
+ function asMarketSettledData(event) {
384
+ return event.data;
385
+ }
386
+ function asLotteryPoolDrawnData(event) {
387
+ return event.data;
388
+ }
389
+ function asLotteryTicketWonData(event) {
390
+ return event.data;
391
+ }
272
392
  // Annotate the CommonJS export names for ESM import in node:
273
393
  0 && (module.exports = {
274
394
  PuulError,
275
395
  PuulPartner,
396
+ asLotteryPoolDrawnData,
397
+ asLotteryTicketWonData,
398
+ asMarketSettledData,
276
399
  asPredictionSettledData,
400
+ isDepositConfirmedEvent,
401
+ isLotteryPoolDrawnEvent,
402
+ isLotteryTicketWonEvent,
403
+ isMarketClosedEvent,
404
+ isMarketSettledEvent,
405
+ isPredictionPlacedEvent,
277
406
  isPredictionSettledEvent,
407
+ isPredictionVoidedEvent,
408
+ isWithdrawalCompletedEvent,
409
+ isWithdrawalFailedEvent,
278
410
  parseWebhookEvent,
279
411
  verifyWebhookSignature
280
412
  });
package/dist/index.mjs CHANGED
@@ -28,6 +28,7 @@ var PuulPartner = class {
28
28
  this.markets = new MarketsAPI(this);
29
29
  this.predictions = new PredictionsAPI(this);
30
30
  this.wallet = new WalletAPI(this);
31
+ this.lottery = new LotteryAPI(this);
31
32
  }
32
33
  // ==========================================================
33
34
  // Internal HTTP layer
@@ -198,6 +199,77 @@ var WalletAPI = class {
198
199
  async getWithdrawalFees(currency, amount) {
199
200
  return this.client.request("GET", `/partner/wallet/withdrawal-fees?currency=${currency}&amount=${amount}`);
200
201
  }
202
+ /**
203
+ * Withdraw from omnibus balance.
204
+ * Initiates a payout to the partner's configured bank account or crypto wallet.
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * const result = await puul.wallet.withdraw({
209
+ * amount: 500000, // $5,000 in cents
210
+ * currency: 'USDC',
211
+ * method: 'bank', // or 'crypto'
212
+ * });
213
+ * ```
214
+ */
215
+ async withdraw(params) {
216
+ return this.client.request("POST", "/partner/wallet/withdraw", params);
217
+ }
218
+ };
219
+ var LotteryAPI = class {
220
+ constructor(client) {
221
+ this.client = client;
222
+ }
223
+ /** List lottery pools, optionally filtered by status and frequency */
224
+ async listPools(options) {
225
+ const params = new URLSearchParams();
226
+ if (options?.status) params.set("status", options.status);
227
+ if (options?.frequency) params.set("frequency", options.frequency);
228
+ if (options?.limit) params.set("limit", String(options.limit));
229
+ const query = params.toString() ? `?${params.toString()}` : "";
230
+ return this.client.request("GET", `/partner/lottery/pools${query}`);
231
+ }
232
+ /** Get a specific pool by ID */
233
+ async getPool(poolId) {
234
+ return this.client.request("GET", `/partner/lottery/pools/${poolId}`);
235
+ }
236
+ /**
237
+ * Buy ticket(s) for a linked user.
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * const result = await puul.lottery.buyTicket('pool-uuid', {
242
+ * userId: 'puul-user-uuid',
243
+ * idempotencyKey: 'lottery-user456-pool1-001',
244
+ * quantity: 3,
245
+ * });
246
+ * ```
247
+ */
248
+ async buyTicket(poolId, params) {
249
+ return this.client.request("POST", `/partner/lottery/pools/${poolId}/buy`, {
250
+ user_id: params.userId,
251
+ idempotency_key: params.idempotencyKey,
252
+ quantity: params.quantity,
253
+ user_external_id: params.userExternalId
254
+ });
255
+ }
256
+ /** List lottery entries for your partner account */
257
+ async listEntries(options) {
258
+ const params = new URLSearchParams();
259
+ if (options?.poolId) params.set("pool_id", options.poolId);
260
+ if (options?.status) params.set("status", options.status);
261
+ if (options?.limit) params.set("limit", String(options.limit));
262
+ const query = params.toString() ? `?${params.toString()}` : "";
263
+ return this.client.request("GET", `/partner/lottery/entries${query}`);
264
+ }
265
+ /** Get a single lottery entry by ID */
266
+ async getEntry(entryId) {
267
+ return this.client.request("GET", `/partner/lottery/entries/${entryId}`);
268
+ }
269
+ /** Get draw results for a completed pool */
270
+ async getDrawResults(poolId) {
271
+ return this.client.request("GET", `/partner/lottery/pools/${poolId}/results`);
272
+ }
201
273
  };
202
274
 
203
275
  // src/webhooks.ts
@@ -224,7 +296,7 @@ function parseWebhookEvent(body) {
224
296
  }
225
297
  const event = body;
226
298
  if (typeof event.event !== "string") {
227
- throw new Error('Invalid webhook body: missing "event" field');
299
+ throw new TypeError('Invalid webhook body: missing "event" field');
228
300
  }
229
301
  return {
230
302
  event: event.event,
@@ -232,17 +304,65 @@ function parseWebhookEvent(body) {
232
304
  data: event.data || {}
233
305
  };
234
306
  }
307
+ function isPredictionPlacedEvent(event) {
308
+ return event.event === "prediction.placed";
309
+ }
235
310
  function isPredictionSettledEvent(event) {
236
311
  return event.event === "prediction.settled";
237
312
  }
313
+ function isPredictionVoidedEvent(event) {
314
+ return event.event === "prediction.voided";
315
+ }
316
+ function isMarketClosedEvent(event) {
317
+ return event.event === "market.closed";
318
+ }
319
+ function isMarketSettledEvent(event) {
320
+ return event.event === "market.settled";
321
+ }
322
+ function isDepositConfirmedEvent(event) {
323
+ return event.event === "deposit.confirmed";
324
+ }
325
+ function isWithdrawalCompletedEvent(event) {
326
+ return event.event === "withdrawal.completed";
327
+ }
328
+ function isWithdrawalFailedEvent(event) {
329
+ return event.event === "withdrawal.failed";
330
+ }
331
+ function isLotteryPoolDrawnEvent(event) {
332
+ return event.event === "lottery.pool.drawn";
333
+ }
334
+ function isLotteryTicketWonEvent(event) {
335
+ return event.event === "lottery.ticket.won";
336
+ }
238
337
  function asPredictionSettledData(event) {
239
338
  return event.data;
240
339
  }
340
+ function asMarketSettledData(event) {
341
+ return event.data;
342
+ }
343
+ function asLotteryPoolDrawnData(event) {
344
+ return event.data;
345
+ }
346
+ function asLotteryTicketWonData(event) {
347
+ return event.data;
348
+ }
241
349
  export {
242
350
  PuulError,
243
351
  PuulPartner,
352
+ asLotteryPoolDrawnData,
353
+ asLotteryTicketWonData,
354
+ asMarketSettledData,
244
355
  asPredictionSettledData,
356
+ isDepositConfirmedEvent,
357
+ isLotteryPoolDrawnEvent,
358
+ isLotteryTicketWonEvent,
359
+ isMarketClosedEvent,
360
+ isMarketSettledEvent,
361
+ isPredictionPlacedEvent,
245
362
  isPredictionSettledEvent,
363
+ isPredictionVoidedEvent,
364
+ isWithdrawalCompletedEvent,
365
+ isWithdrawalFailedEvent,
246
366
  parseWebhookEvent,
247
367
  verifyWebhookSignature
248
368
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puul/partner-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.3.0",
4
4
  "description": "Official TypeScript SDK for the Puul Partner API — integrate prediction markets into your platform",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -42,4 +42,4 @@
42
42
  "engines": {
43
43
  "node": ">=18.0.0"
44
44
  }
45
- }
45
+ }