Partner freeplays
Use /freeplays/* for new integrations. The older /freebets/create and
/freebets/state paths are not accepted by the current canonical partner-signing
guard. This documentation page keeps its /freebets URL for existing bookmarks.
Confirm the deployed sandbox version through capabilities before issuing grants.
Freeplays create a promotion entitlement and do not debit the regular wallet.
Use the same canonical signing as launch and callbacks.
All requests contain operatorSlug and a UUID idempotencyKey, mirrored in headers.
Every authenticated response is signed and uses { "data": ... } or { "error": ... }.
From grant to settlement
A grant is the promotion package; each accepted freeplay is a separate use of that package. Disabling the package does not undo an already accepted use.
POST /freeplays/capabilities
Send this signed body to discover supported settings:
{
"operatorSlug": "your-partner-slug",
"idempotencyKey": "bc47cf38-a1e5-418e-bc66-a6c3c8551628"
}
The response's data includes version: 2, payoutMode: "gross", maxCount: 10000,
payoutCaps and games. Capabilities describe the implementation; confirm that
these features are enabled for your operator, game and currency.
POST /freeplays/create
Use a stable UUID for the grant key and a unique external reference for your campaign
issuance. Set expiresAt to a future UTC time at issuance; replace the illustrative
timestamp below with your campaign's actual expiry.
{
"operatorSlug": "your-partner-slug",
"idempotencyKey": "bc47cf38-a1e5-418e-bc66-a6c3c8551628",
"externalReference": "welcome-campaign-player-001",
"playerId": "fd63c560-f443-49b5-a721-c40c0e06b7be",
"gameSlug": "crash",
"currency": "SOL",
"amount": "0.01",
"totalCount": 10,
"minCoefficient": "2",
"maxWinPerPlay": "0.10",
"maxTotalWin": "0.50",
"expiresAt": "2027-01-01T00:00:00.000Z"
}
| Field | Contract |
|---|---|
externalReference | Required nonempty string, at most 256 characters; unique per operator. |
playerId | Required external player reference, at most 256 characters. Use the same player UUID supplied at launch. |
amount | Required positive decimal string in asset units; up to 16 integer and 8 fractional digits, within currency precision. |
totalCount | Required JSON integer from 1 to 10,000. |
expiresAt | Required future ISO timestamp; use canonical UTC. |
minCoefficient | Optional decimal string, at least 1; supported only for Crash cashout. Omit when unused. |
maxCoefficient | Optional decimal string, at least 1. |
maxWinPerPlay | Optional positive gross payout cap per play, within currency precision. |
maxTotalWin | Optional positive gross payout budget for the whole package. |
maxWin | Deprecated alias of maxWinPerPlay. If both are provided, they must agree. |
gameSettings | Optional game-specific object. Plinko supports allowedRows (integers 8–16) and allowedRisks (low, medium, high), each a nonempty array. |
Omit optional settings when unused. Do not send legacy fields such as securityCode.
A signed HTTP 200 response contains this data shape:
{
"data": {
"id": "f28599df-47da-4b98-b1ac-cc2a3e7c9425",
"grantId": "bc47cf38-a1e5-418e-bc66-a6c3c8551628",
"operatorId": "8e563380-2a27-41b1-8be5-2cdd7d1ec271",
"playerId": "fd63c560-f443-49b5-a721-c40c0e06b7be",
"externalReference": "welcome-campaign-player-001",
"gameSlug": "crash",
"currency": "SOL",
"amount": "0.01",
"maxCoefficient": null,
"maxWin": "0.1",
"maxWinPerPlay": "0.1",
"maxTotalWin": "0.5",
"allocatedWin": "0",
"remainingWinBudget": "0.5",
"payoutMode": "gross",
"minCoefficient": "2",
"gameSettings": {},
"idempotencyKey": "bc47cf38-a1e5-418e-bc66-a6c3c8551628",
"remainingCount": 10,
"totalCount": 10,
"status": "enabled",
"expiresAt": "2027-01-01T00:00:00.000Z",
"createdAt": "2026-10-16T10:00:00.000Z"
}
}
id is the provider enrollment UUID; grantId is your original grant key.
There is no outer version: 2 wrapper on this response. Public statuses are
enabled, completed, expired and cancelled in lowercase.
Decimal formatting is normalized: compare numeric values exactly, not with floats
or string equality ("0.10" and "0.1" represent the same amount).
Verify immutable fields against the original request. Repeating the same key and
settings returns the existing grant's current view, including its current remaining
count and status. A changed payload under the same key returns signed HTTP 409
CONFLICT. Do not create a replacement key to work around an uncertain outcome.
POST /freeplays/state
Look up an uncertain creation by the original grant key:
{
"operatorSlug": "your-partner-slug",
"idempotencyKey": "bc47cf38-a1e5-418e-bc66-a6c3c8551628"
}
You may also provide the provider id. A found grant returns its current data plus
usages, up to the latest 100 records containing betId, status, originalPayout
and payout. This is a recent view, not a complete accounting export.
Only a verified signed HTTP 404 with error.code: "NOT_FOUND" proves absence.
Timeouts, 5xx, malformed JSON and invalid signatures leave the result unknown.
POST /freeplays/cancel
Cancel remaining entitlement by provider id, with a UUID request key:
{
"operatorSlug": "your-partner-slug",
"idempotencyKey": "4b263ca0-e973-4b10-bce2-c72ba9025b83",
"id": "f28599df-47da-4b98-b1ac-cc2a3e7c9425"
}
A successful cancellation returns the grant with status cancelled. Repeating it
returns the current view. Cancellation disables future use; it is not a wallet
refund or cancellation of already accepted bets. Reconcile in-flight usages separately.
Wallet callbacks
Use the ordinary bet, win and cancel paths:
isFree: trueidentifies a freeplay. Require a valid eligible grant.freeplayGrantIdequals the issuanceidempotencyKey/grantId.freeplayEnrollmentIdequals the provider grantid.- Bet
amountis the nominal stake, not zero. Consume one play without debiting the regular wallet. Persist the usage and transaction atomically. - Win
amountis the gross payout, already subject to configured caps. It can includepayoutCoefficient. Apply the agreed payout destination exactly once; do not subtract a regular-wallet stake that was never debited. - Cancel restores eligibility under the grant rules without a regular-wallet credit. Expired/cancelled packages and in-flight usages need explicit reconciliation.
- Retain the original player, session, game, currency, grant and bet relationship. Duplicate callbacks must replay the original result.
Implement promotion accounting separately from the regular wallet. Complete the promotion acceptance tests, including caps, expiry, cancellation and duplicate usage.