Result Callbacks
Callbacks allow the partner to receive coupon settlement changes without continuously polling the API.
Callbacks are optional. The partner may retrieve results only through coupon read methods or combine callbacks with periodic reconciliation.
Enabling callbacks
If the partner wants to use callbacks:
- inform the manager;
- provide the manager with the required connection details;
- the manager enables callbacks and creates the secret phrase
callback_secret; - store the secret on the server side;
- provide the handler URL in
callback_urlwhenever a coupon is placed.
The secret phrase is used to verify the HMAC signature. It is not returned by the Client API and must not be exposed to the browser, logs, or public code.
Delivery conditions
A callback is sent only when all of the following are true:
- the manager enabled callbacks for the account;
- a secret phrase is configured for the account;
- a non-empty
callback_urlwas provided when the specific coupon was placed.
If any condition is not met, retrieve the result through API requests.
Handler URL
The partner defines callback_url and provides it when placing a coupon:
{
"callback_url": "https://partner.example.com/api/coupon-result"
}
Rules:
- use only
https://in production; http://is permitted in the test environment;- one coupon may have only one
callback_url; - different coupons may use different URLs;
- the URL is stored with the coupon and used for its subsequent updates;
- the endpoint must accept
POSTrequests and JSON.
With multi = true, the URL is stored for every single created.
If callbacks are not used, omit the field or provide null, an empty string, or the partner’s main website domain. The main domain is not a callback handler and should be used only when callbacks are disabled.
Network access and IP allowlist
The endpoint must be accessible to incoming requests from the SportAPI Coupon Settlement System.
IP filtering on the partner side is optional. A specific outgoing IP address is not fixed in the public documentation. If the partner wants to use an allowlist, obtain the current address from the manager.
HMAC signature verification is mandatory regardless of IP filtering.
HTTP request
The system sends:
POST {callback_url} HTTP/1.1
Content-Type: application/json
Accept: application/json
X-Coupon-Signature: sha256=<hex_hmac_sha256>
The signature is calculated from the exact bytes of the JSON body. Do not parse and reserialize the JSON before verifying the signature.
The complete algorithm and code examples are provided in Callback signature verification.
Event type
The event field always contains:
coupons.settled
This value is used for:
- final settlement of a single;
- an intermediate accumulator state;
- the first losing bet in an accumulator;
- the final accumulator state;
- returning a coupon for recalculation;
- a new result after recalculation.
The event name does not mean that every received snapshot is necessarily final. Always check coupons[].status and the bet statuses.
Batch delivery
One HTTP request may contain up to 100 coupon snapshots:
{
"couponCount": 2,
"coupons": [
{},
{}
]
}
couponCount is the number of entries in coupons. The handler must iterate over the entire array rather than only the first coupon.
One batch contains snapshots for one account and one callback_url.
Example payload
Example intermediate accumulator state:
{
"event": "coupons.settled",
"batchId": "d407e986f3a64d9d36a77bf532322ef8",
"clientId": 17,
"couponCount": 1,
"coupons": [
{
"coupon_code": "000000000272",
"realWin": 0,
"calculate_coefficient": 0,
"status": 0,
"calculate_date": 1784897500000,
"events_data": [
{
"uuid": "912",
"status": 1,
"calculate_coefficient": 2,
"calculate_date": 1784897500000,
"calculate_score": "2:1",
"settlement_reason_code": "REMOTE_WIN",
"settlement_reason": "Win",
"timer": 0
},
{
"uuid": "913",
"status": 0,
"calculate_coefficient": null,
"calculate_date": null,
"calculate_score": "",
"settlement_reason_code": null,
"settlement_reason": null,
"timer": 0
}
]
}
]
}
In this example, the first bet is settled and the second remains active. The coupon therefore remains at status 0, while realWin and calculate_coefficient are still 0.
Top-level fields
| Field | Type | Description |
|---|---|---|
event | string | Event type. Always coupons.settled. |
batchId | string | Identifier of the specific callback batch version. Used for idempotency. |
clientId | integer | Internal partner account ID. Informational only. |
couponCount | integer | Number of entries in the coupons array. |
coupons | array | Coupon settlement snapshots. |
Coupon snapshot fields
| Field | Type | Description |
|---|---|---|
coupon_code | string | Public coupon code. |
realWin | number | Actual payout. 0 in an intermediate snapshot. |
calculate_coefficient | number | Final settlement multiplier. 0 in an intermediate snapshot. |
status | integer | Current coupon status. |
calculate_date | integer/null | Latest settlement date among the settled bets, as Unix milliseconds. |
events_data | array | All coupon bets, including those not yet settled. |
Bet fields
| Field | Type | Description |
|---|---|---|
uuid | string | Stable internal identifier of the bet within the coupon. |
status | integer | Current bet status. |
calculate_coefficient | number/null | Bet settlement multiplier. |
calculate_date | integer/null | Bet settlement date, as Unix milliseconds. |
calculate_score | string | Settlement score; may be an empty string for an active bet. |
settlement_reason_code | string/null | Stable machine-readable settlement or return reason code; null before settlement. |
settlement_reason | string/null | Settlement or return reason text; null before settlement. |
timer | integer | Compatibility field, currently 0. |
Use settlement_reason_code for analytics and program branching. settlement_reason may contain the original reason text and is primarily intended for storage, logging, or display. The set of codes may grow, so an unknown code must not cause the callback to be rejected.
If the partner displays the result reason to the user, it is better to localize a custom message based on the code. For example, MATCH_POSTPONED means the match was postponed, MATCH_CANCELLED means it was cancelled, and MARKET_PUSH means a return under market rules. For an unknown code, use settlement_reason as fallback text, but not as a key for financial logic.
Data not included in callbacks
A callback contains a settlement snapshot rather than the complete coupon record. It does not include:
amount;- currency;
- sport and tournament names;
- team names;
- market and outcome names;
- the original bet pointer.
However, the callback does include settlement_reason_code and settlement_reason for every bet.
Store the missing data after coupon placement or retrieve it through coupon read methods.
Identifiers
Do not confuse the purposes of these identifiers:
| Field | What it identifies |
|---|---|
coupon_code | The coupon. |
events_data[].uuid | A specific bet within the coupon. |
batchId | A specific callback batch version. |
In the full coupon model, a bet has the numeric field events_data[].id. In a callback, the same ID is provided as a string in uuid.
Recommended relationships in the partner database:
coupon: coupon_code
bet: coupon_code + uuid
callback delivery: batchId
batchId is not a coupon or bet ID.
Multiple callbacks for one coupon
The same coupon_code may normally arrive several times:
- an accumulator is updated as its bets are settled;
- the first loss is sent immediately;
- after all bets in a losing accumulator are settled, the final snapshot arrives;
- recalculation creates a new state.
Every new state receives a new batchId. A repeated HTTP delivery of an existing batch retains the same batchId.
Do not treat another occurrence of coupon_code as a duplicate. Update the coupon state by coupon_code, and identify a repeat of the same delivery by batchId.
Singles and accumulators
For a single, a callback is sent after a result becomes available.
For an accumulator:
- until the accumulator loses, a new snapshot is sent after each subsequent bet is settled;
- on the first losing outcome, a snapshot with coupon status
4is sent immediately; - subsequent intermediate changes to the losing accumulator are not sent;
- after all bets are settled, the final complete snapshot is sent.
The complete scenario is described in Settlement lifecycle.
Recalculation
When a coupon is returned for recalculation:
- the coupon receives status
15; - the corresponding bet receives status
4; - a snapshot with this state is sent immediately;
- after recalculation, a new snapshot with current statuses is sent.
If the previous result has already been financially processed, on first receiving status 15 the partner:
- debits the stake
amountagain; - waits for a new final result;
- credits the new
realWinvalue.
The callback does not contain amount, so store it when the coupon is placed or retrieve it through a read API method.
The financial operation is performed once. A repeated delivery with the same batchId must not debit or credit funds again.
Minimum processing flow
Recommended order:
- read the original request body as bytes;
- verify
X-Coupon-Signature; - begin a transaction;
- store
batchIdwith a unique constraint; - if
batchIdhas already been processed, do not repeat business operations; - process every entry in
coupons; - update coupons by
coupon_codeand bets byuuid; - commit the financial operations;
- commit the transaction;
- return HTTP
200.
See Retries and idempotency for details.
Successful response
Minimum successful response:
HTTP/1.1 200 OK
The body may be empty.
Recommended extended response:
{
"success": true,
"processed": 1
}
The processed value must equal couponCount.
Return HTTP 200 only after every entry in the batch has been committed successfully.
Responses 201, 202, and 204 are not considered successful delivery acknowledgements: the current contract requires HTTP 200 specifically.
Negative or partial acknowledgement
Even with HTTP 200, the response body may indicate an error:
success: false— the batch was not processed;processed < couponCount— only part of the batch was processed.
Such an acknowledgement is treated as a final error and is not retried automatically. Therefore, do not return success: false or a partial processed value with the expectation that delivery will be retried.
Temporary errors must return one of the supported HTTP status codes. The retry schedule and conditions are described in Retries and idempotency.
Next section: Callback signature verification.