Coupon API — cURL request examples
This page presents a verified flow: authenticate, retrieve a current outcome from Sport Line API, create the complete bet pointer, place a single, and retrieve the created coupon.
Before you begin
Install curl and jq. Define the following values only in the current server-side
session:
COUPON_API_BASE_URL='https://YOUR_COUPON_API_DOMAIN'
COUPON_LOGIN='YOUR_LOGIN'
COUPON_PASSWORD='YOUR_PASSWORD'
SPORT_LINE_BASE_URL='https://YOUR_SPORT_LINE_DOMAIN'
SPORT_LINE_PACKAGE='YOUR_SPORT_LINE_API_KEY'
Never commit real values to source control or documentation.
Step 1. Obtain a JWT
LOGIN_RESPONSE=$(curl --silent --show-error --request POST \
--url "$COUPON_API_BASE_URL/api/partner/login" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data "$(jq -nc \
--arg username "$COUPON_LOGIN" \
--arg password "$COUPON_PASSWORD" \
'{username:$username,password:$password}')")
TOKEN=$(printf '%s' "$LOGIN_RESPONSE" | jq -er \
'select(.code == 1) | .body.token')
Do not print $TOKEN to logs. If jq exits with an error, inspect only the safe part
of the response:
printf '%s' "$LOGIN_RESPONSE" | jq \
'{code, error_code, error_message, path}'
See Authentication.
Step 2. Find a sports-line event
Retrieve the current Live menu:
curl --request GET \
--url "$SPORT_LINE_BASE_URL/v1/menu/live/en" \
--header "Package: $SPORT_LINE_PACKAGE" \
--header 'Accept: application/json'
Select sport_id and tournament_id, then retrieve the events:
SPORT_ID='SPORT_ID_FROM_MENU'
TOURNAMENT_ID='TOURNAMENT_ID_FROM_MENU'
curl --request GET \
--url "$SPORT_LINE_BASE_URL/v1/events/$SPORT_ID/$TOURNAMENT_ID/sub/50/live/en" \
--header "Package: $SPORT_LINE_PACKAGE" \
--header 'Accept: application/json'
Read a current game_id from body[].events_list[]. See the
menu and events method
references.
Step 3. Retrieve an outcome and its odds
GAME_ID='GAME_ID_FROM_EVENTS'
EVENT_RESPONSE=$(curl --silent --show-error --request GET \
--url "$SPORT_LINE_BASE_URL/v1/event/$GAME_ID/group/live/en" \
--header "Package: $SPORT_LINE_PACKAGE" \
--header 'Accept: application/json')
Select the first unblocked outcome without a personal player_id:
SELECTION=$(printf '%s' "$EVENT_RESPONSE" | jq -ec '
[.. | objects
| select(
has("oc_pointer") and
(.oc_block == false or .oc_block == 0 or .oc_block == null) and
(.oc_rate | type == "number") and
(.op_id == null)
)
] | first
')
OC_POINTER=$(printf '%s' "$SELECTION" | jq -r '.oc_pointer')
COEFFICIENT=$(printf '%s' "$SELECTION" | jq -r '.oc_rate')
Inspect the selection without exposing credentials:
printf '%s' "$SELECTION" | jq \
'{oc_name, oc_group_name, oc_pointer, oc_rate}'
See event and
Odds and Bet Groups.
Step 4. Create the complete bet pointer
The sports-line oc_pointer contains the technical
game_id|group_id|type_id|rate components, but it is not a complete Coupon API
pointer. Add the line type and current oc_rate for Live:
NORMALIZED_POINTER=${OC_POINTER//|/#}
BET_POINTER="live#$NORMALIZED_POINTER#$COEFFICIENT"
The result has this format:
live#game_id#group_id#type_id#rate#coefficient
For Prematch, query the line feed and use the line prefix. Do not submit stale
odds: retrieve the selected outcome again immediately before placing the coupon.
See Bet pointer.
Step 5. Place a test single
PLACE_RESPONSE=$(curl --silent --show-error --request POST \
--url "$COUPON_API_BASE_URL/api/partner/coupons/place" \
--header 'Accept: application/json' \
--header "Authorization: Bearer $TOKEN" \
--header 'Content-Type: application/json' \
--data "$(jq -nc \
--arg pointer "$BET_POINTER" \
'{
list_bets: [$pointer],
amount: 10,
currency: "USD",
callback_url: null,
lang: "en",
mode: "reject",
mode_type: null,
multi: false
}')")
Print only the essential result fields:
printf '%s' "$PLACE_RESPONSE" | jq '{
code,
error_code,
error_message,
coupons: [.body.coupons[]? | {
coupon_code,
amount,
coef,
status
}]
}'
Success requires a successful HTTP response and code = 1. Store
body.coupons[].coupon_code as a string because it can contain leading zeroes.
See Coupon placement.
Step 6. Place an accumulator
Prepare at least two current pointers from different events:
BET_POINTER_1='live#GAME_ID_1#GROUP_ID#TYPE_ID#RATE#COEFFICIENT'
BET_POINTER_2='live#GAME_ID_2#GROUP_ID#TYPE_ID#RATE#COEFFICIENT'
Do not combine outcomes from a main event and one of its sub-events. Every
list_bets element in an accumulator must belong to a different event.
Submit the pointers with multi: false:
EXPRESS_RESPONSE=$(curl --silent --show-error --request POST \
--url "$COUPON_API_BASE_URL/api/partner/coupons/place" \
--header 'Accept: application/json' \
--header "Authorization: Bearer $TOKEN" \
--header 'Content-Type: application/json' \
--data "$(jq -nc \
--arg pointer1 "$BET_POINTER_1" \
--arg pointer2 "$BET_POINTER_2" \
'{
list_bets: [$pointer1, $pointer2],
amount: 10,
currency: "USD",
callback_url: null,
lang: "en",
mode: "reject",
mode_type: null,
multi: false
}')")
Inspect the accumulator structure:
printf '%s' "$EXPRESS_RESPONSE" | jq '{
code,
error_code,
error_message,
coupons: [.body.coupons[]? | {
coupon_code,
amount,
coef,
coupon_type,
events_count,
status
}]
}'
A successful response contains one body.coupons item, coupon_type = 2, and
events_count = 2. The amount applies to the whole accumulator, while coef is the
combined coefficient.
Step 7. Place separate singles with multi
To create separate singles from the same two pointers, submit multi: true:
MULTI_RESPONSE=$(curl --silent --show-error --request POST \
--url "$COUPON_API_BASE_URL/api/partner/coupons/place" \
--header 'Accept: application/json' \
--header "Authorization: Bearer $TOKEN" \
--header 'Content-Type: application/json' \
--data "$(jq -nc \
--arg pointer1 "$BET_POINTER_1" \
--arg pointer2 "$BET_POINTER_2" \
'{
list_bets: [$pointer1, $pointer2],
amount: 10,
currency: "USD",
callback_url: null,
lang: "en",
mode: "reject",
mode_type: null,
multi: true
}')")
Inspect the separate coupons and total stake:
printf '%s' "$MULTI_RESPONSE" | jq '{
code,
coupon_count: (.body.coupons | length),
total_amount: ([.body.coupons[].amount] | add),
coupons: [.body.coupons[] | {
coupon_code,
amount,
coef,
coupon_type,
events_count,
status
}]
}'
With two pointers, the API returns two singles with different coupon_code values.
The full amount applies to every single: with amount = 10, the total stake is 20,
not 10.
See Singles, accumulators, and multi.
Step 8. Retrieve the coupon
COUPON_CODE=$(printf '%s' "$PLACE_RESPONSE" | jq -er \
'select(.code == 1) | .body.coupons[0].coupon_code')
COUPON_RESPONSE=$(curl --silent --show-error --request GET \
--url "$COUPON_API_BASE_URL/api/partner/coupons/get?coupon_code=$COUPON_CODE" \
--header 'Accept: application/json' \
--header "Authorization: Bearer $TOKEN")
This method returns one coupon directly in body, not in body.coupons. Print the
coupon state and its bet statuses:
printf '%s' "$COUPON_RESPONSE" | jq '
def coupon_status:
{"0":"NEW", "2":"WIN", "4":"LOSE", "8":"RETURN", "15":"UPDATE"}
[tostring] // "UNKNOWN";
def bet_status:
{"0":"NET", "1":"WIN", "2":"LOSE", "3":"RETURN", "4":"RECALCULATE",
"21":"HALF_WIN", "22":"HALF_LOSE", "23":"PUSH"}
[tostring] // "UNKNOWN";
.body | {
coupon_code,
status,
status_name: (.status | coupon_status),
amount,
coef,
calculate_coef,
real_win,
calculate_date,
events: [.events_data[] | {
game_id,
bet_name,
status,
status_name: (.status | bet_status),
coef,
calc_coef,
calculate_score
}]
}
'
Coupon statuses and individual bet statuses use different value tables. For example,
2 means a winning coupon but a losing individual bet. Do not interpret both fields
with one shared mapping.
See Retrieving a coupon.
Step 9. Retrieve active coupons
ACTIVE_RESPONSE=$(curl --silent --show-error --request GET \
--url "$COUPON_API_BASE_URL/api/partner/coupons/active" \
--header 'Accept: application/json' \
--header "Authorization: Bearer $TOKEN")
printf '%s' "$ACTIVE_RESPONSE" | jq '{
code,
count: (.body | length),
coupons: [.body[] | {
coupon_code,
status,
amount,
coef,
coupon_type,
events_count
}]
}'
The coupon array is returned directly in body. An empty array is a successful
response and means that there are currently no active coupons.
Step 10. Retrieve recently settled coupons
CALCULATED_RESPONSE=$(curl --silent --show-error --request GET \
--url "$COUPON_API_BASE_URL/api/partner/coupons/calculated?time=120" \
--header 'Accept: application/json' \
--header "Authorization: Bearer $TOKEN")
printf '%s' "$CALCULATED_RESPONSE" | jq '{
code,
count: (.body | length),
coupons: [.body[] | {
coupon_code,
status,
amount,
calculate_coef,
real_win,
calculate_date
}]
}'
The time parameter selects a window based on final settlement time in minutes. It is
capped at 120 minutes. This method does not return active coupons.
Step 11. Retrieve several known coupons by code
COUPON_CODE_1='FIRST_COUPON_CODE'
COUPON_CODE_2='SECOND_COUPON_CODE'
RESULTS_RESPONSE=$(curl --silent --show-error --request POST \
--url "$COUPON_API_BASE_URL/api/partner/coupons/results" \
--header 'Accept: application/json' \
--header "Authorization: Bearer $TOKEN" \
--header 'Content-Type: application/json' \
--data "$(jq -nc \
--arg code1 "$COUPON_CODE_1" \
--arg code2 "$COUPON_CODE_2" \
'{coupon_ids: [$code1, $code2]}')")
printf '%s' "$RESULTS_RESPONSE" | jq '{
code,
count: (.body.coupons | length),
coupons: [.body.coupons[] | {
coupon_code,
status,
amount,
real_win,
coupon_type,
events_count
}]
}'
The batch method returns its array in body.coupons. The response can contain fewer
coupons and use a different order, so match records by coupon_code, never by array
index.
See Active and settled coupons, Querying coupon results, and Statuses and payouts.
Common errors
| Result | Cause | Action |
|---|---|---|
HTTP 401 | The JWT is absent or no longer valid | Sign in again once |
error_code = 11 | The pointer is incomplete or malformed | Check the line type and coefficient |
error_code = 501 | The odds changed | Refresh the outcome and apply the selected mode rules |
error_code = 502 | The outcome disappeared | Ask the user to select another outcome |
error_code = 503 | The outcome is blocked | Do not retry before refreshing the line |
Do not blindly retry coupon placement after a network timeout. The first request may have succeeded, and a retry can create a duplicate coupon.