puul_partner_sdk

Official Dart/Flutter SDK for the Puul Partner API — integrate prediction markets and lottery draws into your mobile app.

Installation

# pubspec.yaml
dependencies:
  puul_partner_sdk: ^1.5.0

Quick Start

import 'package:puul_partner_sdk/puul_partner_sdk.dart';

final puul = PuulPartner(
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
);

// List live markets
final markets = await puul.markets.list();

// Place a prediction
final prediction = await puul.predictions.place(
  marketId: markets.first.id,
  outcomeId: markets.first.outcomes.first.id,
  stakeAmount: 100000,    // ₦1,000 in minor units
  stakeCurrency: 'NGN',
  idempotencyKey: 'unique-key-001',
);

print('Estimated return: ${prediction.estimatedReturn}');

API Reference

Sessions (User Linking)

// Create a link token
final linkToken = await puul.sessions.createLinkToken(
  externalUserId: 'user-123',
  email: 'user@example.com',
  countryCode: 'NG',
);

// Exchange for a session
final session = await puul.sessions.create(linkToken.linkToken);

Markets

// List all live markets
final markets = await puul.markets.list();

// Filter by country
final ngMarkets = await puul.markets.list(countries: ['NG']);

Predictions

// Get a quote (locks odds for 10 seconds)
final quote = await puul.predictions.createQuote(
  marketId: 'market-uuid',
  outcomeId: 'outcome-uuid',
  stakeAmount: 100000,
  stakeCurrency: 'NGN',
);

// Place with locked quote
final prediction = await puul.predictions.place(
  marketId: 'market-uuid',
  outcomeId: 'outcome-uuid',
  stakeAmount: 100000,
  stakeCurrency: 'NGN',
  idempotencyKey: 'unique-key',
  quoteId: quote.quoteId,
);

// Check result
final result = await puul.predictions.get(prediction.id);

Wallet

final balance = await puul.wallet.getBalance();
final omnibus = await puul.wallet.getOmnibusBalance(currency: 'NGN');
final account = await puul.wallet.getDepositAccount();

// Withdraw from omnibus to configured bank/crypto destination
final withdrawal = await puul.wallet.withdraw(
  amount: 500000,     // $5,000 in minor units
  currency: 'USDC',
  method: 'bank',     // or 'crypto'
);
print('Withdrawal ID: ${withdrawal.withdrawalId}');

Lottery

// List open lottery pools
final pools = await puul.lottery.listPools(status: 'open');
print('Open pools: ${pools.length}');

// Get pool details
final pool = await puul.lottery.getPool(pools.first.id);
print('Pool: ${pool.title} — Ticket price: ${pool.ticketPrice}');

// Buy ticket(s) for a linked user
final ticket = await puul.lottery.buyTicket(
  poolId: pool.id,
  userExternalId: 'user-123',
  idempotencyKey: 'lottery-user123-pool1-001',
  quantity: 3,
);
print('Bought ${ticket.entries.length} ticket(s)');

// List lottery entries
final entries = await puul.lottery.listEntries(poolId: pool.id);
for (final entry in entries.entries) {
  print('Entry ${entry.id}: ${entry.status}');
}

// Get draw results
final results = await puul.lottery.getDrawResults(pool.id);
print('Winning numbers: ${results.winningNumbers}');

Auto-Settlement: If your payout_method is configured (via the admin dashboard), revenue share is automatically paid out after each market settlement.

Error Handling

try {
  await puul.predictions.place(...);
} on PuulError catch (e) {
  print(e.code);       // 'INSUFFICIENT_BALANCE'
  print(e.message);    // 'Insufficient wallet balance'
  print(e.statusCode); // 400
  print(e.retryable);  // false
}

Webhook Verification

import 'package:puul_partner_sdk/puul_partner_sdk.dart';

final isValid = verifyWebhookSignature(
  rawBody: request.body,
  signature: request.headers['x-puul-signature']!,
  secret: 'your-webhook-secret',
);

if (isValid) {
  final event = parseWebhookEvent(jsonDecode(request.body));
  if (event.isPredictionSettled) {
    print('Prediction settled: ${event.data}');
  } else if (event.isPredictionVoided) {
    print('Prediction voided: ${event.data}');
  } else if (event.isWithdrawalCompleted) {
    print('Withdrawal completed: ${event.data}');
  }
}

Configuration

final puul = PuulPartner(
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  baseUrl: 'https://api.joinpuul.com/api/v1',  // default
  timeout: Duration(seconds: 30),               // default
);

Cleanup

puul.dispose(); // Close the HTTP client when done

Libraries

puul_partner_sdk
Official Dart/Flutter SDK for the Puul Partner API.