Odds, Bets, and Coupons — API Technical Documentation
This file contains a concise standalone guide for connecting to the SportAPI Coupon Settlement System. Details and rare cases are in full documentation.
POST /api/partner/login
POST /api/partner/coupons/place
GET /api/partner/coupons/calculated?time=10 1. What you need to get from the manager
- base URL API;
- client account login and password;
- when using callback - enable the function and the secret phrase
callback_secret.
The examples use a conditional address:
BASE_URL="https://coupon-api.example.com"
In production, use HTTPS. All dates are transmitted as Unix timestamp in milliseconds, and monetary values are transmitted as decimal numbers without fixed precision.
2. API response format
Success:
{
"code": 1,
"body": {},
"error_code": null,
"error_message": null,
"date": 1784970000000,
"time_ms": 5,
"path": "/api/partner/example"
}
The business error usually also comes with HTTP 200:
{
"code": 0,
"body": null,
"error_code": 1002,
"error_message": "Not all params",
"date": 1784970000000,
"time_ms": 5,
"path": "/api/partner/example"
}
Always check the HTTP code, then code, then error_code. Do not use the text error_message as a software key.
3. Authorization
POST /api/partner/login
Content-Type: application/json
{
"username": "partner-demo",
"password": "strong-password"
}
The field login is supported as a compatible alias username.
Successful response:
{
"code": 1,
"body": {
"token": "<jwt-token>",
"user_id": 17,
"username": "partner-demo"
},
"error_code": null,
"error_message": null
}
In all protected requests, pass:
Authorization: Bearer <jwt-token>
Basic login errors:
| Code | Reason |
|---|---|
1002 | Login or password not sent. |
1003 | Unknown login or incorrect password. |
1004 | The client account has been disabled. |
1006 | Access date has expired. |
1007 | The client balance is zero or negative. |
These errors are returned from HTTP 200, code = 0. HTTP 401 of a protected method means that the JWT is missing, invalid, expired, or revoked; Sign in again. HTTP 403 means inappropriate role or access denied.
4. Bet pointer
Each selected outcome is transmitted as a ready-made line from the sports line:
line_type#game_id#group_id#type_id#rate#coefficient[#player_id]
Examples:
line#737779544#1#1#0#1.85
live#738917381|119|5869|0.5#3.6#149439538
line_type:lineorlive;rate: total/handicap parameter or0;player_id: optional player ID;- delimiters
#and|are supported.
Do not collect or correct the pointer manually - pass the value obtained from the line unchanged.
5. Coupon placement
POST /api/partner/coupons/place
Authorization: Bearer <jwt-token>
Content-Type: application/json
{
"list_bets": [
"line#737779544#1#1#0#1.85"
],
"amount": 10,
"currency": "USD",
"callback_url": "https://partner.example.com/api/coupon-result",
"lang": "en",
"mode": "reject",
"mode_type": null,
"multi": false
}
| Field | Mandatory | Purpose |
|---|---|---|
list_bets | Yes | One or more pointers. |
amount | Yes | The positive amount of one coupon being created. |
currency | No | Any string designation, including virtual currency. |
callback_url | No | URL callback; without a callback you can not send it, you can pass null, an empty string or a site domain. |
lang | No | A two-letter code for one of approximately 50 supported languages. The language of names is fixed upon creation. |
mode | No | reject or accept; default reject. |
mode_type | For accept | Allowed direction of the odds change. |
multi | No | One general coupon or individual singles; default false. |
Odds
| Settings | Behavior |
|---|---|
mode = reject | Reject creation when odds change. |
mode = accept, mode_type = 1 | Accept only odds increases. |
mode = accept, mode_type = 2 | Accept only odds decreases. |
mode = accept, mode_type = 3 | Accept any change. |
Singles, accumulators and multi
| Request | Result | Debit of client balance |
|---|---|---|
| One outcome | One single | amount |
Multiple outcomes, multi = false | One accumulator | amount |
Multiple outcomes, multi = true | Separate single for each outcome | amount × number of created coupons |
One accumulator train can contain no more than 15 events. You cannot combine multiple bets on the same match, including the main match, halves, periods, corners, fouls and other related sub-events. This combination returns 506.
6. Confirmation of coupon and bet placement
Full successful response with coupon and accepted bet data inside events_data:
{
"code": 1,
"body": {
"coupons": [
{
"coupon_code": "000000000272",
"amount": 10,
"win": 18.5,
"potential_win": 18.5,
"real_win": null,
"coef": 1.85,
"original_coef": 1.85,
"calculate_coef": null,
"has_return": false,
"date": 1784970000000,
"status": 0,
"asian": false,
"calculate_date": null,
"coupon_type": 1,
"events_count": 1,
"events_data": [
{
"id": 912,
"game_id": 737779544,
"main_game_id": 737779544,
"is_sub_game": false,
"parent_game_id": null,
"sub_game_key": null,
"raw_pointer": "line#737779544#1#1#0#1.85",
"line_type": "line",
"is_live": false,
"bet_group_id": 1,
"bet_group_name": "Match result",
"bet_id": 1,
"bet_name": "First team to win",
"sport_id": 1,
"sport_name": "Football",
"tournament_id": 10001,
"tournament": "National League",
"event_date": 1784971800000,
"status": 0,
"opp1": "Team A",
"opp2": "Team B",
"coef": 1.85,
"calc_coef": null,
"bet_score": "line#737779544#1#1#0#1.85",
"calculate_date": null,
"calculate_score": null,
"settlement_reason_code": null,
"settlement_reason": null,
"placement_score_full": null,
"placement_score_periods": null,
"calculation_score_full": null,
"calculation_score_periods": null,
"timer": null,
"dop_name": null,
"rate": "0",
"sgame_id": null,
"game_num": null,
"stat_id": null,
"team1_id": 101,
"team2_id": 102,
"opp_icon1": 101,
"opp_icon2": 102
}
]
}
]
},
"error_code": null,
"error_message": null,
"date": 1784970000015,
"time_ms": 45,
"path": "/api/partner/coupons/place"
}
Coupon fields
Each item body.coupons[] is a separate coupon created.
| Field | Type | Meaning |
|---|---|---|
coupon_code | string | Public 12 digit coupon code. Store as a string to avoid losing leading zeros. |
amount | number | The coupon amount transferred upon creation. |
win | number | Current displayed winning amount. |
potential_win | number | Possible winnings before final settlement. |
real_win | number/null | Actual payout amount after calculation; before calculation - null. |
coef | number | Current or final coupon ratio. |
original_coef | number | Total coupon odds at the time of placement. |
calculate_coef | number/null | Final settlement multiplier; before settlement — null. |
has_return | boolean | true, if the coupon contains a bet with a refund status. |
date | integer | Coupon creation date, Unix timestamp in milliseconds. |
status | integer | Current coupon status. The values are described in the “Coupon and bet statuses” section. |
asian | boolean | Sign of the presence of Asian settlement, including half winnings or losses. |
calculate_date | integer/null | Coupon calculation date in milliseconds; before calculation - null. |
coupon_type | integer | Coupon type: 1 — single, 2 — accumulator. |
events_count | integer | Number of bets inside the coupon. |
events_data | array | The full array of bets included in the coupon. |
Bet fields
Each element of events_data[] describes a specific accepted bet within the coupon.
| Field | Type | Meaning |
|---|---|---|
id | integer/null | Internal ID of the accepted bet. Used in conjunction with coupon_code to find a bet within a coupon. |
game_id | integer | ID of the event or sub-event on which the bet is placed. |
main_game_id | integer/null | ID of the main match to which the event or sub-event belongs. |
is_sub_game | boolean | true, if the bet relates to a half, period, set, corners or other sub-event. |
parent_game_id | integer/null | The ID of the immediate parent event, if one exists. |
sub_game_key | string/null | Technical key of a sub-event or period. |
raw_pointer | string | The original bet pointer accepted by the API. |
line_type | string | Line type: line - pre-match, live - real-time event. |
is_live | boolean | true, if the bet was created on a live line. |
bet_group_id | integer | ID of the bet group or market. |
bet_group_name | string/null | Localized name of the bet group. |
bet_id | integer | ID of the selected selection within the bet group. |
bet_name | string/null | Localized full name of the selected selection. |
sport_id | integer/null | Sport ID. |
sport_name | string/null | Localized name of the sport. |
tournament_id | integer/null | Tournament ID. |
tournament | string/null | Localized name of the tournament. |
event_date | integer/null | Event start date, Unix timestamp in milliseconds. |
status | integer | Current bet settlement status. Not to be confused with coupon status. |
opp1 | string/null | Name of the first team or participant. |
opp2 | string/null | Name of the second team or participant. |
coef | number | The bet odds at which the bet was accepted. |
calc_coef | number/null | Calculated bet multiplier; before calculation - null. |
bet_score | string | Legacy API compatible field containing the bet indicator rather than the match score. |
calculate_date | integer/null | Bet settlement date in milliseconds; before calculation - null. |
calculate_score | string/null | The account used to calculate the bet. |
settlement_reason_code | string/null | Stable machine code of the reason for settlement or refund; before calculation - null. |
settlement_reason | string/null | Explanatory text of the reason for settlement or refund; before calculation - null. |
placement_score_full | string/null | Total event score at the time the bet was created, if available. |
placement_score_periods | string/null | Score by period at the time the bet was created, if available. |
calculation_score_full | string/null | The total score of the event at the time the bet is settled. |
calculation_score_periods | string/null | Score by period at the time of bet settlement. |
timer | integer/null | Event timer at the time of data saving, if available. |
dop_name | string/null | Name of the sub-event: half, period, set, inning, etc. |
rate | string | Outcome parameter, for example, the value of the total or handicap; for an outcome without a parameter - "0". |
sgame_id | string/null | Sub-event foreign key. |
game_num | integer/null | Game number, if provided by the source. |
stat_id | string/null | External statistical event ID. |
team1_id | integer/null | ID of the first team or participant. |
team2_id | integer/null | ID of the second team or participant. |
opp_icon1 | integer/null | Compatible ID of the first command icon; matches team1_id. |
opp_icon2 | integer/null | Compatible second team icon ID; matches team2_id. |
The value null is normal for data that has not yet been calculated or is not available. Do not automatically replace it with 0 or the empty string.
Consider the coupon accepted only if code = 1 and the presence of objects in body.coupons. Before this, the basket is a preliminary choice: the selection could disappear, be blocked or the odds change.
After success:
- save all
body.couponsobjects, not just the first one; - store
coupon_codeas a string with leading zeros; - save the required data from
events_data; - match the coupon to the end user;
- record the user’s financial transaction in the partner’s system.
Identifiers:
| Field | Purpose |
|---|---|
coupon_code | Public coupon code. |
events_data[].id | ID of the specific accepted bet inside the coupon. |
callback events_data[].uuid | The same bet ID, passed as a string. |
batchId | ID of the callback batch version, not the coupon or bet. |
currency is not returned in the full model and callback. If the currency is needed, save the value from the create request.
7. Creation errors
| Code | Reason | Action |
|---|---|---|
10 | No request body. | Correct the request. |
11 | Invalid pointer. | Get the current pointer from the line. |
12 | Invalid amount. | Pass a positive number. |
501 | The odds have changed. | Show new value or change mode. |
502 | The outcome is unavailable. | Remove or update the bet in the bet slip. |
503 | The outcome is blocked. | Report temporary unavailability. |
504 | Outcome verification error. | Do not consider the coupon accepted; repeat later. |
506 | In an accumulator contains bets from the same match. | Leave one outcome or use separate singles. |
507 | Insufficient client balance. | Top up your balance or reduce the total amount. |
1002 | Invalid parameter set. | Correct parameters. |
10000 | Internal error. | Record the error and check the result before retrying. |
For 501–504, the new API returns the affected bets in body.changes[]. Field change_type: 1 — the odds increased, 2 — the odds decreased, null — direction does not apply.
Don’t blindly retry POST /coupons/place after timeout: the first request may have been accepted and retrying will create a duplicate.
8. Receiving coupons
| Operation | Endpoint | Result |
|---|---|---|
| One coupon | GET /api/partner/coupons/get?coupon_code={code} | Coupon at body. |
| Active | GET /api/partner/coupons/active | Array in body[]. |
| Recent calculations | GET /api/partner/coupons/calculated?time=10 | Array in body[]; maximum 120 minutes. |
| By code/period | POST /api/partner/coupons/results | Array in body.coupons. |
| Client balance | GET /api/partner/balance | body.balance. |
You can transfer up to 100 values using codes:
{
"coupon_ids": ["000000000272", "000000000273"]
}
Or pass the creation period no more than 24 hours:
{
"start_date": 1784880000000,
"end_date": 1784966400000
}
Do not combine coupon_ids and dates in the same query. results filters by creation time, and calculated filters by final settlement time.
9. Coupon and bet statuses
Coupon statuses:
| Code | Meaning | Final |
|---|---|---|
0 | Active or partially settled. | No |
2 | Won. | Yes |
4 | Lost. | Yes |
8 | Fully refunded. | Yes |
15 | Returned for recalculation; expect a new result. | No |
Bet statuses:
| Code | Meaning | calc_coef |
|---|---|---|
0 | Not calculated. | null |
1 | Win. | Original odds |
2 | Losing. | 0 |
3 | Return. | 1 |
4 | Awaiting recalculation. | null |
21 | Half the winnings. | (coef + 1) / 2 |
22 | Half loss. | 0.5 |
23 | Push. | 1 |
For financial accrual to the end user, use the ready-made real_win. Do not use potential_win and do not recalculate the payout yourself. Before calculation, real_win, calculate_coef, calc_coef and calculate_date are equal to null, not 0.
At the first status of the coupon 15, if the previous result has already been financially processed, the partner once again debits amount from the end user, expects a new final status and charges a new real_win. Protect operations from reprocessing.
10. Reason for calculation
Each bet in the full model and callback has:
| Field | Purpose |
|---|---|
settlement_reason_code | Stable settlement/refund reason code. |
settlement_reason | Explanatory source text. |
Before calculation, both fields are equal to null. For a localized notification, use code like:
MATCH_POSTPONED- the match has been postponed;MATCH_CANCELLED- match cancelled;MARKET_PUSH- return according to market rules.
If the code is unknown, save it and use the non-empty settlement_reason as a fallback text. Do not calculate the payout from the reason - use statuses and real_win.
These fields explain the calculation or return that has already been performed and are not a separate realtime feed of the match status.
11. Callback
Callback is optional. A partner can only work through polling. For callback, the manager must enable the function and create callback_secret; The URL is sent in every coupon created.
The partner determines the callback URL independently. In production it should use https://; in a test environment, http:// is allowed.
The system sends:
POST {callback_url}
Content-Type: application/json
X-Coupon-Signature: sha256=<hex_hmac_sha256>
Short payload:
{
"event": "coupons.settled",
"batchId": "d407e986f3a64d9d36a77bf532322ef8",
"couponCount": 1,
"coupons": [
{
"coupon_code": "000000000272",
"realWin": 18.5,
"calculate_coefficient": 1.85,
"status": 2,
"calculate_date": 1784973600000,
"events_data": [
{
"uuid": "912",
"status": 1,
"calculate_coefficient": 1.85,
"calculate_date": 1784973600000,
"calculate_score": "2:1",
"settlement_reason_code": "REMOTE_WIN",
"settlement_reason": "Win",
"timer": 0
}
]
}
]
}
Mandatory rules:
- calculate
sha256=<hex(HMAC-SHA256(raw_body, callback_secret))>from the exact source bytes of the body before JSON parsing and compare signatures in a secure way; - process all items
coupons, the package can contain up to 100 coupons; - store
batchIdwith a unique index; - repeated
batchIdshould not repeat the debit or accrual; - one
coupon_codecan come with differentbatchIdduring progressive calculation and recalculation; - return HTTP
200only after the entire packet has been securely stored.
The minimum successful response is an empty HTTP 200. Advanced:
{
"success": true,
"processed": 1
}
processed must equal couponCount. Responses 201, 202, and 204 are not considered successful.
Replays are performed only in case of timeout, transport error or HTTP 500, 502, 503, 504: immediately, then after 1, 5, 15 and 60 minutes - no more than five sendings. There is no automatic retry on HTTP 200 with success: false or partial processed.
12. Polling fallback
Even with callback, periodically check the results:
GET /api/partner/coupons/calculated?time=10
After a break of more than 120 minutes, use POST /api/partner/coupons/results over saved coupon_ids or creation periods up to 24 hours. Receiving the same state again should not repeat financial transactions.
13. Security and storage
- store login, password, JWT and
callback_secretonly on the server; - do not pass JWT in the URL and do not write the full token in the logs;
- use decimal type for money;
- store
coupon_codeas a string; - save
amount, the required bet data andcurrency, if used; - distinguish between coupon and bet statuses;
- accept unknown fields and reason codes without error;
- Make all end-user financial transactions idempotent.
14. Old API and Cashout
Old routes continue to be supported, but new integrations must use /api/partner/**. Old answers and errors have different formats. To update an existing client, use a single file “Migrating from the old API”.
Cashout is in development, not fully tested and not recommended for production.
15. Final checklist
- Received
BASE_URL, login and password. - JWT is transmitted as a Bearer token.
- The pointers are taken from the line without modification.
- The cart is saved as an accepted coupon only after
code = 1. - Processed
multi, accumulator restrictions and errors501–507. - The automatic debit of the client balance has been taken into account.
- All
coupon_codeand bet IDs are saved. - The final payout is taken from
real_win. - The callback is checked against the source bytes and deduplicated using
batchId. - Backup polling has been configured.
- The user balance is separate from the client balance SportAPI.
Detailed documentation begins with API overview. The complete endpoint map is in manual.