OllinDocs
API reference

Postflop endpoints

The family catalog, family detail, board NodeDocuments, and the 501 tree slot — with real responses.

All examples below are real responses from the API serving the trimmed real-producer fixtures (fixtures-real/), elided () for length. One adjustment: the fixtures trim each family to 2 boards, so board_count and the boards list are restored here to their production value (184).

GET /v1/families

The postflop catalog — one summary per family the serving tier currently carries. In the GCS layout the list refreshes as the extraction ETL uploads packs (on MANIFEST_TTL_MS, both on access and via a background interval).

GET /v1/families → 200
{
  "campaign": "berserk_v1",
  "stamp": "20260721T131354Z",
  "tier": "v0-roots",
  "families": [
    {
      "id": "100bb_3bet_call_p7v5_n13380",
      "depth_bb": 100,
      "line": "3bet_call",
      "positions": { "oop": "p7", "ip": "p5" },
      "pot_bb": 51.6943,
      "eff_bb": 100,
      "tier": "root",
      "board_count": 184
    },
    { "id": "100bb_4bet_call_p7v5_n13443", "…": "…" }
  ]
}

positions uses HRC seat indices (p7 = BB, p5 = BTN). The family id is the arriving-library slug: {depth}bb_{line}_p{oop}v{ip}_n{hrcNodeId}.

GET /v1/families/:fam

Family detail: solve config, preflop ranges, and the board index.

GET /v1/families/100bb_3bet_call_p7v5_n13380 → 200
{
  "family": { "id": "100bb_3bet_call_p7v5_n13380", "depth_bb": 100, "…": "…" },
  "config": {
    "bet_sizes_pct": [25, 55, 80, 125],
    "raise_sizes_pct": [55],
    "accuracy_target_pct_pot": 0.3,
    "solver": "solve_config=tiny via extract_roots_v0.py"
  },
  "ranges": { "oop": { "AhAd": 1, "…": "…" }, "ip": { "…": "…" } },
  "boards": ["2s2h2d", "…", "Ks7s6s"],
  "notes": [
    "family preflop range weights are unavailable in sealed root data — served membership-only (uniform weight 1)",
    "per-board IP range weights are unavailable — served uniform; documents carry meta.ip_weights_uniform=true",
    "bb values derived with bb_chips = eff_stack_chips / depth_bb (meta.bb_chips_derived=true); effective_stack_bb === depth_bb by construction"
  ]
}

notes (optional) is where the v0 adapter states its caveats in prose — the same facts the per-board documents carry as v1.1 meta flags.

GET /v1/families/:fam/boards/:flop

The full NodeDocument v1 for the board's root node. Board ids are normalized (ks7s6sKs7s6s).

GET /v1/families/100bb_3bet_call_p7v5_n13380/boards/ks7s6s → 200
{
  "schema_version": 1,
  "family_id": "100bb_3bet_call_p7v5_n13380",
  "board": "Ks7s6s",
  "node_path": "root",
  "street": "flop",
  "actor": "oop",
  "pot_bb": 51.6943,
  "effective_stack_bb": 100,
  "actions": [
    { "id": "check", "kind": "check" },
    { "id": "bet_25", "kind": "bet", "amount_bb": 12.9236, "fraction_pot": 0.25 },
    { "id": "bet_55", "kind": "bet", "amount_bb": 28.4319, "fraction_pot": 0.55 },
    { "id": "bet_80", "kind": "bet", "amount_bb": 41.3554, "fraction_pot": 0.8 },
    { "id": "bet_125", "kind": "bet", "amount_bb": 64.6179, "fraction_pot": 1.25 },
    { "id": "allin", "kind": "allin", "amount_bb": 100, "fraction_pot": 1.93445 }
  ],
  "ranges": {
    "oop": { "4c3c": 0.0682, "…": "…" },
    "ip": { "5c4c": 1, "…": "…" }
  },
  "strategy": { "4c3c": [0, 0.998825, 0.001175, 0, 0, 0], "…": "…" },
  "evs": { "4c3c": [14.8319, 15.5388, 14.9497, 12.4406, 4.6278, -0.1049], "…": "…" },
  "evs_current": { "4c3c": 15.5381, "…": "…" },
  "grid169": [
    {
      "class": "AA", "row": 0, "col": 0, "weight": 6,
      "strategy": [0.021477, 0.941234, 0.035977, 0.001312, 0, 0],
      "ev": [47.0729, 47.3414, 47.1934, 47.0528, 46.6779, 46.2034]
    },
    { "…": "168 more cells, row-major" }
  ],
  "exploitability": { "exploitability_bb": 0.1464, "exploitability_pct_pot": 0.2832 },
  "meta": {
    "campaign": "berserk_v1",
    "stamp": "20260721T131354Z",
    "tier": "root",
    "pack_sha256": "38b207591014bfb929478985269e04643814ff5bbef9d958b927f0530b0bdc13",
    "solver": "solve_config=tiny via extract_roots_v0.py",
    "ip_weights_uniform": true,
    "bb_chips_derived": true,
    "bb_chips": 80.86
  }
}

Field-by-field semantics: NodeDocument v1.

GET /v1/families/:fam/boards/:flop/node

The contract slot for non-root nodes. Tier root has no tree data, so this returns a typed 501 — deliberately, with the future already spelled out. Tiers tree and live will serve identical NodeDocument v1 responses from this exact URL.

GET …/boards/ks7s6s/node?path=root.bet_55 → 501
{
  "error": {
    "code": "TIER_NOT_AVAILABLE",
    "message": "Family \"100bb_3bet_call_p7v5_n13380\" is served at tier \"root\", which only carries root-node documents. Non-root nodes arrive with tier \"tree\" (full-tree boardpacks) and tier \"live\" (real-time solves) and will be served from this exact endpoint as NodeDocument v1. The root document is available now at /v1/families/100bb_3bet_call_p7v5_n13380/boards/Ks7s6s.",
    "details": {
      "tier": "root",
      "requested_path": "root.bet_55",
      "available_paths": ["root"],
      "planned_tiers": ["tree", "live"]
    }
  }
}

This 501 is what the strategy browser's locked tree slots map to.

On this page