Portal
Sign In Console

PLO Strategy Endpoint

This page documents how to query the strategy API for PLO (Pot Limit Omaha) through the POST /lookup endpoint. It is self-contained: it covers every request and response field, with a full request and response example.

Endpoint

Endpoint: POST /lookup

All requests require an authorization token in the Authorization header:

curl --location '<glue-URL>/lookup' \
--header 'Content-Type: application/json' \
--header 'Authorization: <Auth-TOKEN>' \
--data '{ ... }'

The actual <glue-URL> for your environment (dev / stg / prod) and the <Auth-TOKEN> are provided separately.

Possible HTTP error statuses: 400 (bad request), 401 (missing Authorization header), 403 (invalid token), 422 (validation error), 502 (upstream error). Note that some engine-level failures are reported inside an HTTP 200 body via the error / error_code response fields — clients should always check them (see Response Fields).


Selecting the PLO Variant

The variant is selected with hand.game_type — the hole-card count is part of the game type:

game_typeVariantHero hole cardsPlayers
"plo4"Pot Limit Omaha, 4 cards42–8
"plo5"Pot Limit Omaha, 5 cards52–6
"plo6"Pot Limit Omaha, 6 cards62–5

These are the only accepted values — there is no bare "plo". hand.game_mode_code is always "normal": PLO is served as a cash game only.

PLO specifics:

  • Full 52-card deck; standard Omaha rules (exactly 2 hole cards + 3 board cards make a hand).
  • Pot-limit betting — the action menu never exceeds a pot-sized raise.
  • 2-blind and 3-blind (straddle) structures and antes are supported, as is rake configuration via rake_ratio_pct / rake_cap_bb.

Card Notation

Cards use a two-character notation: uppercase rank (A K Q J T 9 8 7 6 5 4 3 2) followed by lowercase suit (c d h s). A hand is the concatenation of its cards with no separators. Board cards are sent as entries of the action list (see the Action object below), also concatenated: "Tc6hKc" for a flop, "Qd" for a turn or river.

game_typehole_cards exampleLength
plo4"9hAhJs9c"8 chars (4 cards)
plo5"7cQs9h9dTc"10 chars (5 cards)
plo6"8cQhTs2d9h4h"12 chars (6 cards)

A hole-card string whose length does not match the game type is rejected with error E106 (e.g. sending 2 cards with game_type: "plo4"). Only the hero — the player the strategy is requested for — carries hole_cards; every other player must omit the field.


Request Format

{
  "request_id": "unique-request-id",
  "hand": { <Hand-object> },
  "protocol_ver": "v2"
}

Request Fields

Top Level

FieldTypeRequiredDescription
request_idstringNoCaller-chosen identifier, echoed back inside the response's request echo. Use it to correlate requests; use hand.gameuuid to track whole hands (a hand spans multiple requests).
handHand-objectYesThe full game state — players, blinds, seats and the action history. See the Hand object below.
protocol_verstringYesProtocol version. Use "v2" for PLO.

Hand Object

"hand": {
  "gameuuid": "<hand-unique-identifier>",
  "game_type": "<plo4 | plo5 | plo6>",
  "game_mode_code": "normal",
  "players": [ <Player-object> ],
  "actions": { "entries": [ <Action-object> ] },
  "big_blind": <big-blind-size>,
  "small_blind": <small-blind-size>,
  "ante": <ante-size>,
  "dealer_seat": <dealer-player-seat>,
  "sb_seat": <small-blind-player-seat>,
  "bb_seat": <big-blind-player-seat>,
  "straddle_seat": <straddle-seat-or-minus-1>
}
FieldTypeRequiredDescription
gameuuidstringYesUnique hand identifier for tracking. Any value is accepted.
game_typestringYes"plo4", "plo5" or "plo6" (see Selecting the PLO Variant).
game_mode_codestringYesAlways "normal" for PLO.
playersPlayer-objectsYesList of player objects — see below.
actionsAction-objectsYesChronological list of everything that happened in the hand so far, including board cards — see below.
big_blindintegerYesBig blind value in actual chips.
small_blindintegerNoSmall blind in chips. When absent it is derived from the big blind.
anteintegerYesAnte per player in chips; 0 when the game has no ante.
dealer_seatintegerYesDealer (button) seat.
sb_seatintegerYesSmall blind seat.
bb_seatintegerYesBig blind seat.
straddle_seatintegerYesStraddle seat for 3-blind games; -1 when there is no straddle.
rake_ratio_pctdoubleNoRake percentage. Explicit 0 means rake-free; absent applies the server default.
rake_cap_bbdoubleNoRake cap in big blinds. Explicit 0 means uncapped.
roomidstringNoCaller room/table identifier, passed through.

Player Object

"players": [
  {
    "seat_no": <player-seat-number>,
    "stack": <chip-amount>,
    "hole_cards": "<hero-cards-or-field-removed>"
  }
]
FieldTypeRequiredDescription
seat_nointegerYesThe player's seat number. Must match the seat indexes used in the hand fields (sb_seat, bb_seat, dealer_seat, straddle_seat) and in action entries.
stackintegerYesThe player's chip count at the start of the hand, before blinds and antes.
hole_cardsstringYes (hero only)The hero's hole cards in card notation — 4, 5 or 6 cards as dictated by game_type (e.g. "9hAhJs9c" for plo4). Exactly one player must carry this field; remove it for all other players.
uidstringNoCaller player identifier, passed through.

Action Object

"actions": {
  "entries": [
    { "action": "raise", "amount": 1000, "seat_no": 3 },
    { "action": "call", "seat_no": 4 },
    { "action": "Tc6hKc" },
    { "action": "check", "seat_no": 3 }
  ]
}

entries is a single flat chronological list mixing player actions and board deals:

FieldTypeRequiredDescription
actionstringYesEither an action verb — fold, check, call, bet, raise, allin — or, for a board deal, the dealt cards concatenated (e.g. "Tc6hKc" for the flop, "Qd" for turn/river).
seat_nointegerYes for player actionsSeat of the acting player. Removed on board-deal entries — that is how the two entry kinds are distinguished.
amountintegerFor bet/raise/allinChips added by the player in this action on the current street.

The street is implied by the position of the board-deal entries — there is no explicit street field. An empty list ("entries": []) means it is preflop and the first player is to act. Blinds, antes and straddles are not listed as entries; they are derived from the hand fields.


Response Format

{
  "request": { <echo-of-the-request> },
  "strategy": {
    "actions": [ <StrategyAction> ],
    "decision_action_seq": { <ActionList> },
    "suggested_action": { <ActionDetails> }
  },
  "supportScore": <0-to-1>,
  "solution_information": { <Solution-information-object> },
  "strategy_profile": null,
  "warnings": [],
  "flags": []
}

Response Fields

Top Level

FieldTypeDescription
requestobjectEcho of the request.
strategyStrategy-objectThe strategy solution at the decision point.
supportScoredoubleModel confidence that it covers this spot, 0 to 1. 0.5 is the neutral default.
solution_informationobjectDiagnostics, including situation_key — how the engine classified the spot (game_type, street, flop, players_bin, bb_depth).
strategy_profileobjectnull — strategy profiles are not applied to PLO.
warningsstring[]Human-readable warnings.
flagsstring[]Machine-readable processing flags.
error / error_codestringPresent on engine soft failures even with HTTP 200 — e.g. "error_code": "E500" with an explanatory error text. Always check these before consuming strategy.

Strategy Object

FieldTypeDescription
actionsStrategyAction[]All candidate actions for the hero with their probabilities and EVs.
decision_action_seqAction-objectsThe action line of the decision point (echo of the request's entries, normalized).
suggested_actionActionDetailsThe single action the engine recommends taking — the sampled/most likely action of the strategy.

Strategy Action Object

FieldTypeDescription
actionActionDetailsThe concrete action: action verb (fold, check, call, bet, raise, allin), amount — the raise-to / bet chip amount (null for fold/check/call), pot_size — the bet as a ratio of the pot if called, and seat_no of the hero.
action_namestringCompact action code: f, c, x, b&lt;pct&gt;, r&lt;pct&gt;, allin&lt;pct&gt; where the number is the pot percentage — "b75" is a bet of 75% pot, "b100" a pot-sized bet.
action_seqAction-objectsThe full action line ending in this candidate action (the request's entries plus this action appended).
hand_strategyfloatProbability the strategy assigns to this action for the hero hand, 0 to 1.
hand_evfloatExpected value of taking this action, in big blinds.

Request Example

5-handed PLO4 with a straddle (seat 2) and a 50-chip ante. Preflop, seat 3 raised to 1,000, the hero (seat 4, on the button, holding 9hAhJs9c) called, everyone else folded. The flop is Tc6hKc; seat 3 checked and the hero is to act:

curl --location '<glue-URL>/lookup' \
--header 'Content-Type: application/json' \
--header 'Authorization: <Auth-TOKEN>' \
--data '{
  "request_id": "plo4-flop-demo",
  "hand": {
    "gameuuid": "plo4_5p_game",
    "game_type": "plo4",
    "game_mode_code": "normal",
    "players": [
      { "seat_no": 0, "stack": 5000 },
      { "seat_no": 1, "stack": 31900 },
      { "seat_no": 2, "stack": 19000 },
      { "seat_no": 3, "stack": 131620 },
      { "seat_no": 4, "stack": 41370, "hole_cards": "9hAhJs9c" }
    ],
    "big_blind": 100,
    "ante": 50,
    "dealer_seat": 4,
    "sb_seat": 0,
    "bb_seat": 1,
    "straddle_seat": 2,
    "actions": {
      "entries": [
        { "action": "raise", "amount": 1000, "seat_no": 3 },
        { "action": "call", "seat_no": 4 },
        { "action": "fold", "seat_no": 0 },
        { "action": "fold", "seat_no": 1 },
        { "action": "fold", "seat_no": 2 },
        { "action": "Tc6hKc" },
        { "action": "check", "seat_no": 3 }
      ]
    }
  },
  "protocol_ver": "v2"
}'

Reading the request: hole_cards is 8 characters — exactly 4 cards, matching "plo4". The straddle is declared with straddle_seat: 2 (making this a 3-blind game); blinds, straddle and antes are not repeated in the action entries — the first entry is already the first voluntary action (seat 3's raise). The flop deal "Tc6hKc" carries no seat_no, and seat 3's check after it leaves the hero to act.

Response Example

{
  "request": { ... },
  "strategy": {
    "actions": [
      {
        "action": { "action": "fold", "amount": null, "pot_size": null, "seat_no": 4 },
        "action_name": "f",
        "action_seq": { ... },
        "hand_strategy": 0.0,
        "hand_ev": -10.5
      },
      {
        "action": { "action": "check", "amount": null, "pot_size": null, "seat_no": 4 },
        "action_name": "x",
        "action_seq": {
          "entries": [
            { "action": "raise", "amount": 1000, "seat_no": 3 },
            { "action": "call", "seat_no": 4 },
            { "action": "fold", "seat_no": 0 },
            { "action": "fold", "seat_no": 1 },
            { "action": "fold", "seat_no": 2 },
            { "action": "Tc6hKc" },
            { "action": "check", "seat_no": 3 },
            { "action": "check", "seat_no": 4 }
          ]
        },
        "hand_strategy": 0.7657038121459327,
        "hand_ev": 13.478801727294922
      },
      {
        "action": { "action": "bet", "amount": 850, "pot_size": 0.32692307233810425, "seat_no": 4 },
        "action_name": "b33",
        "action_seq": { ... },
        "hand_strategy": 0.0013135409935110887,
        "hand_ev": 13.478801727294922
      },
      {
        "action": { "action": "bet", "amount": 1300, "pot_size": 0.5, "seat_no": 4 },
        "action_name": "b50",
        "action_seq": { ... },
        "hand_strategy": 0.231213544309503,
        "hand_ev": 13.478801727294922
      },
      {
        "action": { "action": "bet", "amount": 1950, "pot_size": 0.75, "seat_no": 4 },
        "action_name": "b75",
        "action_seq": { ... },
        "hand_strategy": 0.0017282659289257333,
        "hand_ev": 13.478801727294922
      },
      {
        "action": { "action": "bet", "amount": 2600, "pot_size": 1.0, "seat_no": 4 },
        "action_name": "b100",
        "action_seq": { ... },
        "hand_strategy": 0.00004083662212741418,
        "hand_ev": 13.478801727294922
      }
    ],
    "decision_action_seq": { "entries": [ ...same-entries-as-the-request... ] },
    "suggested_action": {
      "action": "check",
      "amount": null,
      "pot_size": null,
      "seat_no": 4
    }
  },
  "supportScore": 0.7780449986457824,
  "solution_information": {
    "situation_key": {
      "game_type": "plo4",
      "street": "flop",
      "flop": "Tc6hKc",
      "players_bin": "5",
      "bb_depth": 50.0
    }
  },
  "strategy_profile": null,
  "warnings": [],
  "flags": []
}

({ ... } marks elided parts: the full request echo, the repeated action_seq objects, and the diagnostic members of solution_information.)

How to read it: the suggested_action is a check, which the strategy plays 76.6% of the time (hand_strategy: 0.7657); the main alternative is a half-pot bet of 1,300 chips ("b50", 23.1%). The remaining bet sizes carry negligible probability. All bet sizes stop at pot_size: 1.0 — pot-limit betting means the menu never exceeds a pot-sized raise. Folding when checking is free would be a blunder: probability 0 and an EV of −10.5 big blinds, far below the +13.48 bb of the played actions.


Limitations

  • PLO is served as a cash game onlygame_mode_code is always "normal".
  • Player counts: PLO4 2–8; PLO5 2–6; PLO6 2–5.
  • Hole-card count must match game_type — 4 / 5 / 6 cards; mismatches fail with E106.
  • Soft failures can arrive with HTTP 200 — check error / error_code in the response body.
  • Use protocol_ver: "v2" for PLO requests.