> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fieldfunded.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a Bet Settlement Engine with Market Resolution

> Build an automatic bet settlement engine with market resolution using FieldFunded's API. Poll, match, resolve, and pay — 20 lines of code.

# Build a Bet Settlement Engine with Automatic Market Resolution

FieldFunded's market resolution API settles bets automatically — poll resolved markets, match by ID, pay out winners. No manual checking, no missed results. This guide builds a production-ready settlement engine in under 100 lines of TypeScript.

## What You'll Use

| SDK Method         | Endpoint                     | Purpose                           |
| ------------------ | ---------------------------- | --------------------------------- |
| `getSettlements()` | `GET /v1/settlements`        | Poll for resolved markets         |
| `getEventResult()` | `GET /v1/events/{id}/result` | Get final score                   |
| `checkBet()`       | `POST /v1/bets/check`        | Verify individual bet outcome     |
| `checkParlay()`    | `POST /v1/bets/check-parlay` | Verify parlay/accumulator outcome |

## Architecture

```
Game Ends → Poll getSettlements() every 60s → Match bets by market_id + selection_id → Resolve (won/lost/refund) → Update balance
```

## Step 1: Set Up the SDK

```typescript theme={null}
import { FieldFundedSDK } from '@fieldfunded/sdk';

const client = new FieldFundedSDK({
  apiKey: process.env.FIELDFUNDED_API_KEY!,
  baseUrl: 'https://api.fieldfunded.com/v1',
});
```

## Step 2: Poll for Settlements

FieldFunded's settlement is **gradual**. Simple markets (1x2, Winner) resolve within 2-5 minutes of game end. Complex markets (player props, correct score) take 10-30 minutes.

```typescript theme={null}
async function pollSettlements() {
  const data = await client.getSettlements({ limit: 20 });

  for (const settlement of data.settlements) {
    console.log(
      `Event ${settlement.event_id}: ${settlement.settlement_status}`,
      `— ${settlement.resolved_markets_count}/${settlement.total_markets_count} markets`,
      `(${settlement.pending_markets_count} pending)`
    );

    // Process only when we have resolved markets
    if (settlement.resolved_markets_count > 0) {
      await processSettlement(settlement);
    }
  }
}
```

## Step 3: Match Bets Against Resolved Markets

The key is matching by `market_id` and `selection_id` — these are unique identifiers that guarantee you're resolving the correct bet.

```typescript theme={null}
async function processSettlement(settlement: any) {
  // Get all unresolved bets for this event from your database
  const pendingBets = await db.query(
    'SELECT * FROM bets WHERE event_id = $1 AND status = $2',
    [settlement.event_id, 'pending']
  );

  for (const bet of pendingBets) {
    // Use checkBet for precise resolution
    const result = await client.checkBet({
      selections: [{
        event_id: bet.event_id,
        market: bet.market_name,
        outcome: bet.outcome,
        odds: bet.odds,
        stake: bet.stake,
        market_id: bet.market_id,       // Use IDs for 100% accuracy
        selection_id: bet.selection_id,
      }],
    });

    const betResult = result.results[0];

    if (betResult.result === 'pending') continue; // Not yet resolved

    await resolveBet(bet, betResult);
  }
}
```

## Step 4: Resolve and Pay

```typescript theme={null}
async function resolveBet(bet: any, result: any) {
  switch (result.result) {
    case 'won':
      // Payout = stake × odds (returned by API)
      await db.query(
        'UPDATE bets SET status = $1, payout = $2 WHERE id = $3',
        ['won', result.payout, bet.id]
      );
      await db.query(
        'UPDATE users SET balance = balance + $1 WHERE id = $2',
        [result.payout, bet.user_id]
      );
      break;

    case 'lost':
      await db.query(
        'UPDATE bets SET status = $1, payout = 0 WHERE id = $2',
        ['lost', bet.id]
      );
      break;

    case 'refund':
      // Return original stake
      await db.query(
        'UPDATE bets SET status = $1, payout = $2 WHERE id = $3',
        ['refunded', bet.stake, bet.id]
      );
      await db.query(
        'UPDATE users SET balance = balance + $1 WHERE id = $2',
        [bet.stake, bet.user_id]
      );
      break;
  }
}
```

## Step 5: Handle Parlays

For parlays, use `checkParlay()`. The API handles void legs automatically (void leg = odds 1.0, recalculates combined odds).

```typescript theme={null}
async function resolveParlay(parlay: any) {
  const result = await client.checkParlay({
    legs: parlay.legs.map((leg: any) => ({
      event_id: leg.event_id,
      market: leg.market_name,
      outcome: leg.outcome,
      odds: leg.odds,
      market_id: leg.market_id,
      selection_id: leg.selection_id,
    })),
    stake: parlay.stake,
  });

  // result.payout contains recalculated odds if any leg was voided
  console.log(`Parlay status: ${result.payout.status}`);
  console.log(`Combined odds: ${result.payout.combined_odds}`);
  console.log(`Payout: ${result.payout.payout}`);
}
```

## Step 6: Edge Cases

### Postponed Games

FieldFunded follows the **48-hour rule**: if a game is postponed but resumes within 48 hours, bets remain active. After 48 hours, all markets are refunded.

### Cancelled / Walkover

Immediate refund. The `getEventResult()` endpoint returns `status: 'walkover'` or `status: 'cancelled'`.

```typescript theme={null}
const result = await client.getEventResult(eventId);
if (['walkover', 'cancelled', 'abandoned'].includes(result.status)) {
  await refundAllBets(eventId);
}
```

### Timeout Protection

If some markets haven't resolved after 30 minutes, they auto-refund. Your settlement loop simply picks up the refund status on the next poll.

## Step 7: Run the Loop

```typescript theme={null}
import cron from 'node-cron';

// Poll every 60 seconds
cron.schedule('* * * * *', async () => {
  try {
    await pollSettlements();
  } catch (err) {
    console.error('Settlement poll failed:', err);
  }
});
```

## Production Checklist

* [ ] Use `market_id` + `selection_id` for matching (not market names)
* [ ] Make resolution idempotent (re-processing won't double-pay)
* [ ] Log every resolution for audit trail
* [ ] Handle SDK errors (RateLimitError, ServiceUnavailableError)
* [ ] Set up alerts if settlement polling stops

<Card title="Settlement API Reference" icon="book" href="/api-reference/settlement/get-settlements">
  See full endpoint documentation →
</Card>

***

## Related Guides

* [Build a Betting Platform](/guides/build-betting-platform) — full-stack architecture that uses this engine
* [Build a Parlay Calculator](/guides/build-parlay-calculator) — validate parlays with real odds
* [FieldFunded vs Sportradar](/compare/vs-sportradar) — why auto settlement is our biggest edge

<CardGroup cols={2}>
  <Card title="Get Your Free API Key" icon="key" href="/quickstart">
    Start building in 5 minutes — 10,000 free requests/month
  </Card>

  <Card title="See Pricing" icon="dollar-sign" href="/compare/pricing">
    All plans compared side by side
  </Card>
</CardGroup>
