Equity Actions API
One endpoint for hand-aware Hold’em action probabilities and conditional bet sizing.
/api/equity_actionsMake a decision request
Send the acting player’s exact hand and the public game state immediately before a decision. The API combines learned public-context action frequencies with equity-based allocation and returns an action distribution. Your application samples the action and applies its game rules.
This public reference covers equity actions only. Range tracking and range updates are not part of this endpoint.
Authentication
Call the API from your backend using JSON and an x-api-key header. Keep the key on the server, out of browser bundles and public repositories. Use the deployment origin supplied with your access as the base URL; append /api/equity_actions.
Content-Type: application/json
x-api-key: YOUR_SERVER_SIDE_API_KEYExamples use HOLDEM_API_URL and HOLDEM_API_KEY as application environment variables. Public key generation and account access are coming later.
Request body
Send the following fields only. All numbers must be finite.
| Field | Description |
|---|---|
policy | Required object containing schemaVersion, context, history, and legalMask. |
policy.schemaVersion | Required literal: holdem-public-board-joint-v1. |
policy.context | Required array of exactly 316 encoded public features. See the encoding reference below. |
policy.history | Required chronological array of up to 512 events, each exactly 32 numbers. Include only events before the decision. An empty array is supported. |
policy.legalMask | Required [fold, passive, aggressive] array. Each value is 0 or 1, and at least one action must be allowed. Passive means call/check; aggressive means raise/bet. Use actual engine legality. Folding must be disabled when no bet is faced. |
hand | Required exact combo, such as AsAh. Ranks: 23456789TJQKA; suits: cdhs. Cards must differ and cannot overlap the board. |
range | Optional exact-combo map of nonnegative candidate weights for the actor. At most 1,326 entries; weights need not sum to 1, but their total must be finite. The actual hand must retain positive mass. |
gateSlope | Optional finite number greater than 0 and at most 200; defaults to 20. Controls the steepness of the equity-based continuation and aggression gates. |
style | Optional: tight-passive, tight-aggressive, loose-passive, or loose-aggressive. Requires a configured compatible style model. Omit to use the public policy. |
Game-state encoding
The current wire format uses numeric vectors, not a raw game-state object. Keep seat IDs consistent across context and history. Use zero-based seat indexes 0–8 and street order preflop, flop, turn, river. The forthcoming SDK will be documented separately when its equity-actions integration is available.
Context · 316 values
| Index (zero-based) | Description |
|---|---|
0–259 | Five 52-value board slots in board order. A present card is one-hot; an unused slot is all zeros. Card index = rankIndex × 4 + suitIndex, using ranks 23456789TJQKA and suits cdhs. Slots must be contiguous, without duplicate cards. |
260–268 | Nine-value one-hot acting seat. |
269–272 | Four-value one-hot street. Board length must match: 0, 3, 4, or 5 cards. |
273–278 | Scaled potBB, facingBB, callBB, minRaiseToBB, maxRaiseToBB, and seatCount, in that order. Raise-to values are street totals, not additional payments. |
279–314 | Nine seat records of four values: present (0/1), scaled remaining stackBB, scaled contributionBB on the current street, active (0/1). Absent seats use four zeros. |
315 | Number of remaining active opponents divided by 9. |
Apply Math.log1p(Math.max(0, value)) / Math.log1p(400) to every scaled value, including seatCount. Amounts before scaling are in big blinds. Do not scale one-hot fields, boolean flags, or the final opponent fraction.
History · 32 values per event
| Index (zero-based) | Description |
|---|---|
0–8 | One-hot acting seat. |
9–12 | One-hot street. |
13–18 | One-hot action, ordered posts, folds, checks, calls, bets, raises. |
19–22 | Scaled amountAddedBB, raiseToBB (0 when absent), livePostBB (0 when absent), deadPostBB (0 when absent). amountAddedBB is the payment added by this event. |
23–31 | Scaled refunds after the event, one total per seat. |
Use the same scaling function as context. Preserve all preceding events in order; do not include the action being predicted or future cards and outcomes. Histories longer than 512 events are rejected.
Server-side request example
This complete encoding example represents a heads-up preflop decision. Replace the state with your engine’s actual snapshot and configure your deployment URL and credential. It uses native fetch; it is not an SDK example.
// Server-side JavaScript. Illustrative heads-up preflop state:
// SB acts after posting 0.5 BB; BB has posted 1 BB.
const scale = (n) => Math.log1p(Math.max(0, n)) / Math.log1p(400);
const oneHot = (index, width) =>
Array.from({ length: width }, (_, i) => Number(i === index));
const post = (seat, amount) => [
...oneHot(seat, 9), ...oneHot(0, 4), ...oneHot(0, 6),
...[amount, 0, amount, 0].map(scale), ...Array(9).fill(0),
];
const context = [
...Array(260).fill(0), // No board cards preflop
...oneHot(0, 9), // Actor: seat 0
...oneHot(0, 4), // Street: preflop
...[1.5, 0.5, 0.5, 2, 100, 2].map(scale),
1, scale(99.5), scale(0.5), 1, // Seat 0
1, scale(99), scale(1), 1, // Seat 1
...Array(7 * 4).fill(0), // Seven absent seats
1 / 9, // One remaining opponent
];
const response = await fetch(
new URL('/api/equity_actions', process.env.HOLDEM_API_URL),
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.HOLDEM_API_KEY,
},
body: JSON.stringify({
policy: {
schemaVersion: 'holdem-public-board-joint-v1',
context,
history: [post(0, 0.5), post(1, 1)],
legalMask: [1, 1, 1], // Fold, call, raise
},
hand: 'AsAh',
gateSlope: 20,
}),
},
);
const result = await response.json();
if (!response.ok || !result.ok) {
throw new Error(result.error ?? 'Decision request failed');
}
console.log(result.output, result.sizes, result.weights);Response & sizing
Facing a bet, output contains fold, call, and raise. Otherwise it contains check and bet. Probabilities sum to one, with illegal actions at zero.
// Illustrative response excerpt, not a complete response
{
"ok": true,
"hand": "AsAh",
"equity": 0.84,
"facingBet": true,
"output": { "fold": 0.02, "call": 0.26, "raise": 0.72 },
"population": { "fold": 0.40, "passive": 0.35, "aggressive": 0.25 },
"gateSlope": 20
}| Field | Description |
|---|---|
ok / requestId | Success flag and request identifier. |
hand / equity / facingBet | Canonical acting hand, its uniform heads-up equity benchmark (0–1), and whether a bet is faced. |
output | Final action probabilities for the actual hand. No action is sampled by the API. |
population | Original model targets: fold, passive, aggressive, before hand-specific equity allocation. |
sizes / weights | Parallel arrays of size labels and conditional aggression probabilities. Sample from these only after choosing bet or raise. Weights sum to one. |
gateSlope / style | The gate slope used, and the requested style when supplied. |
expectedValues | Simplified showdown benchmark in BB, including assumptions, action output, and per-size paymentBB/evBB. Illegal or unusable values can be null. This is not a strategic EV prediction. |
learnedExpectedValues | Optional experimental server-configured output. Predicts historical observed-action returns from public context; alternative-action scores are not validated counterfactual EVs. Does not change the policy. |
Size classes
["22%", "34%", "47%", "63%", "85%", "112%", "147%", "200%", "280%+", "all-in"]Percentages mean chips paid this action divided by the pot before the action. They are not raise-to totals. The 280%+ class is open-ended; all-in is a separate class. Convert the sampled size to a legal payment in your engine, enforcing minimum raises, reopening, stack limits, rounding, and side pots.
Expected-value assumptions
The showdown benchmark uses uniform heads-up equity, one opponent always calling aggression, no future betting, no rake, and no side pots. Checking assumes a free showdown; the open-ended size uses 280% as its representative amount. It does not estimate fold equity or changes in calling ranges. Do not replace the returned action policy with these estimates.
Ranges & equity allocation
The optional range is the acting player’s candidate population, not the opponent’s range. Board-blocked combos are removed and reversed card order is canonicalized. Do not send opponents’ actual cards. Do not reduce the candidate population to only the actual hand: that removes equity differentiation.
Omitting range uses equally weighted physical combos compatible with the board. This population is not conditioned on preceding action history. The endpoint does not return an updated range.
Candidate equities use a uniform heads-up opponent benchmark, not opponent-range-specific or multiway equity. Continuation and aggression gates assign stronger-equity hands less folding and more aggression while matching the model’s weighted population targets. Equal-equity hands receive equal probabilities; passive probability can peak in the middle. Sizing itself is not equity-gated. This policy is not a guarantee of optimal or profitable play.
Errors
| HTTP / code | Description |
|---|---|
400 BAD_REQUEST | Invalid JSON, unsupported fields, wrong vector sizes, inconsistent board/street, invalid hand, legal mask, range, style, or gate slope. Fix the request before retrying. |
401 Unauthorized | Missing or invalid x-api-key. Authentication failures use an error field and may not include ok or requestId. |
502 EQUITY_UNAVAILABLE | The equity dependency failed or returned invalid/incomplete results. Placeholder equity is not returned. |
503 MODEL_UNAVAILABLE | The required public or style model is missing or unusable. |
503 ACTION_VALUE_MODEL_UNAVAILABLE | An explicitly enabled experimental action-value model could not be used. |
503 authentication unavailable | Server-side API authentication is not configured. The response contains an error message. |
{
"ok": false,
"requestId": "request-identifier",
"error": "BAD_REQUEST",
"message": "Supplementary validation detail"
}Endpoint errors include ok: false, requestId, and error; message is optional. Inspect the HTTP status as well as the body. Handle failures explicitly rather than treating them as decisions.
JavaScript & TypeScript SDK
A server-side SDK will be available for integrating equity actions into your application. The existing SDK targets Node.js 20+, provides TypeScript declarations, and has no runtime dependencies.
For now, the HTTP request above is the integration reference. Keep API calls and credentials on your backend.
Back to HoldemAPI