SportAPI Documentation
EN
C Product documentationCoupon API
v1
Service & pricing ↗ Get access ↗
Coupon API / Accumulator integration

End-to-End Example: Accumulator

An accumulator is one coupon that combines several bets on different matches.

The main differences from a single are:

  • list_bets contains several pointers;
  • multi must be false;
  • one coupon_code is created;
  • amount applies to the entire accumulator;
  • one amount is automatically deducted from the SportAPI client balance;
  • the partner separately deducts one amount from the end user in its own system;
  • bets may be settled at different times;
  • one coupon may receive several successive callbacks.

Overall Flow

The user selects outcomes from several different matches

The partner creates one accumulator bet slip

POST /api/partner/coupons/place
multi = false

code = 1

One coupon
Automatic deduction of amount from the SportAPI client balance
Separate deduction of amount from the user in the partner system

Successive bet status changes

Final result + payout to the user according to real_win

Accumulator Restrictions

Validate the bet slip before submitting it:

  • an accumulator must contain at least two bets;
  • one accumulator may contain no more than 15 events;
  • the bets must belong to different matches;
  • a main event cannot be combined with one of its sub-events;
  • different sub-events of the same match cannot be combined.

Forbidden combinations include:

  • the match result and corners in the same match;
  • the match result and a separate half;
  • corners and fouls in the same match;
  • different periods, sets, or other sub-events of the same match.

To determine whether outcomes belong to the same match, consider not only game_id, but also the relationship with the main event. The SportAPI Coupon Settlement System performs the final validation as well.

If this rule is violated, the API returns:

error_code = 506

Step 1. Obtain a JWT

Authenticate through:

POST /api/partner/login

The following variables are used in the examples below:

BASE_URL="https://coupon-api.example.com"
TOKEN="<jwt-token>"

The partner receives the actual BASE_URL and credentials from the manager.

See Authentication for details.

Step 2. Prepare the Pointers

In this example, the user selected three outcomes from three different matches:

line#737779544#1#1#0#1.80
line#737880112#8#6#2.5#1.50
line#738004921#17#9#3.5#2.00

Each pointer must be obtained from a selected sports-line outcome and submitted without modification.

Initial odds:

1.80 × 1.50 × 2.00 = 5.40

For an amount of 10, the initial potential payout is:

10 × 5.40 = 54

After successful creation, the API returns the actual original_coef and potential_win. Store and display the response values, not only the preliminary calculation from the bet slip.

Step 3. Show the Bet Slip to the User

Before confirmation, the partner interface must clearly show:

  • that one accumulator will be created;
  • the three selected bets;
  • the odds of each bet;
  • the preliminary total odds;
  • the amount of the single accumulator;
  • the potential payout;
  • the rule for accepting changed odds.

In this example:

type: accumulator
bets: 3
amount: 10 USD
total amount to deduct: 10 USD
preliminary odds: 5.40
preliminary payout: 54 USD

The bet slip is a preliminary selection, not an accepted coupon.

Step 4. Create the Accumulator

curl --request POST \
  --url "$BASE_URL/api/partner/coupons/place" \
  --header "Accept: application/json" \
  --header "Authorization: Bearer $TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "list_bets": [
      "line#737779544#1#1#0#1.80",
      "line#737880112#8#6#2.5#1.50",
      "line#738004921#17#9#3.5#2.00"
    ],
    "amount": 10,
    "currency": "USD",
    "callback_url": "https://partner.example.com/api/coupon-result",
    "lang": "en",
    "mode": "reject",
    "mode_type": null,
    "multi": false
  }'

The key field is:

{
  "multi": false
}

It combines all three list_bets entries into one accumulator.

If callbacks are not used, omit callback_url or submit null.

Do Not Confuse This with multi = true

For the same three pointers:

ModeResultTotal amount
multi = falseOne accumulator containing three bets10
multi = trueThree separate singles10 × 3 = 30

multi = true does not create an accumulator. The full amount applies to every single created.

Step 5. Process Successful Creation

Shortened response example:

{
  "code": 1,
  "body": {
    "coupons": [
      {
        "coupon_code": "000000000350",
        "amount": 10,
        "win": 54,
        "potential_win": 54,
        "real_win": null,
        "coef": 5.4,
        "original_coef": 5.4,
        "calculate_coef": null,
        "has_return": false,
        "date": 1784970000000,
        "status": 0,
        "calculate_date": null,
        "coupon_type": 2,
        "events_count": 3,
        "events_data": [
          {
            "id": 912,
            "game_id": 737779544,
            "raw_pointer": "line#737779544#1#1#0#1.80",
            "bet_name": "First team to win",
            "status": 0,
            "coef": 1.8,
            "calc_coef": null,
            "calculate_date": null
          },
          {
            "id": 913,
            "game_id": 737880112,
            "raw_pointer": "line#737880112#8#6#2.5#1.50",
            "bet_name": "Total over 2.5",
            "status": 0,
            "coef": 1.5,
            "calc_coef": null,
            "calculate_date": null
          },
          {
            "id": 914,
            "game_id": 738004921,
            "raw_pointer": "line#738004921#17#9#3.5#2.00",
            "bet_name": "Total over 3.5",
            "status": 0,
            "coef": 2,
            "calc_coef": null,
            "calculate_date": null
          }
        ]
      }
    ]
  },
  "error_code": null,
  "error_message": null,
  "date": 1784970000015,
  "time_ms": 48,
  "path": "/api/partner/coupons/place"
}

Verify:

code = 1
body.coupons.length = 1
coupon_type = 2
events_count = 3
events_data.length = 3

Only after these checks:

  1. store the coupon;
  2. store all three bets;
  3. store coupon_code as a string;
  4. record one deduction of amount = 10 from the end user in the partner system;
  5. mark the local coupon as accepted.

Recommended bet keys:

000000000350 + 912
000000000350 + 913
000000000350 + 914

Do not make three financial deductions from the end user: these are bets within one accumulator, not separate coupons. SportAPI has already deducted one amount from the client balance upon successful creation.

currency is not included in the response or callback model. If it is required, store USD from the original request.

If the Accumulator Is Rejected

When code = 0:

  • do not store the bet slip as an accepted coupon;
  • do not perform the final deduction;
  • release the preliminary reservation if one was used;
  • process error_code and body.changes.

Multiple Bets from the Same Match

Example:

{
  "code": 0,
  "body": null,
  "error_code": 506,
  "error_message": "<bets from the same match cannot be combined>",
  "date": 1784970000000,
  "time_ms": 12,
  "path": "/api/partner/coupons/place"
}

Update the bet slip so that only one outcome remains from each match.

Changed or Unavailable Outcome

Any accumulator entry may fail validation:

error_codeReason
501The odds changed.
502The outcome is absent from the current feed.
503The outcome is blocked.
504Outcome validation failed.
507The client balance is insufficient to create the accumulator.

Problematic entries are returned in body.changes. In reject mode, a change to even one set of odds prevents the original accumulator from being accepted.

With 507, the accumulator is not created and the SportAPI client balance remains unchanged.

Step 6. Store the Initial State

Immediately after creation:

coupon status = 0
bet statuses = [0, 0, 0]
real_win = null
calculate_coef = null
calculate_date = null

Status 0 means that the accumulator has not received a final result yet.

Step 7. Process Successive States

Accumulator bets may finish at different times. Every callback contains a complete current snapshot of all bets.

For this accumulator, the sequence may be:

[0, 0, 0] → coupon created
[1, 0, 0] → first bet won
[1, 3, 0] → second bet returned
[1, 3, 1] → third bet won; accumulator completed

Here:

  • 0 — the bet is unsettled;
  • 1 — win;
  • 3 — return.

First Intermediate State

After the first bet wins, the callback may look like this:

{
  "event": "coupons.settled",
  "batchId": "batch-version-001",
  "clientId": 17,
  "couponCount": 1,
  "coupons": [
    {
      "coupon_code": "000000000350",
      "realWin": 0,
      "calculate_coefficient": 0,
      "status": 0,
      "calculate_date": 1784973600000,
      "events_data": [
        {
          "uuid": "912",
          "status": 1,
          "calculate_coefficient": 1.8,
          "calculate_date": 1784973600000,
          "calculate_score": "2:1",
          "timer": 0
        },
        {
          "uuid": "913",
          "status": 0,
          "calculate_coefficient": null,
          "calculate_date": null,
          "calculate_score": "",
          "timer": 0
        },
        {
          "uuid": "914",
          "status": 0,
          "calculate_coefficient": null,
          "calculate_date": null,
          "calculate_score": "",
          "timer": 0
        }
      ]
    }
  ]
}

The partner:

  1. verifies the HMAC;
  2. stores the new batchId;
  3. updates bet 912;
  4. leaves the coupon active;
  5. does not credit realWin = 0, because status = 0;
  6. returns HTTP 200.

realWin = 0 in an intermediate snapshot does not mean that the coupon lost.

Second Intermediate State

The second bet is returned:

batchId = batch-version-002
coupon status = 0
bet statuses = [1, 3, 0]

This is a new state of the same coupon:

  • coupon_code remains 000000000350;
  • batchId changes;
  • bet 913 receives a settlement multiplier of 1;
  • no financial payout may be made yet.

The return of one bet does not return the whole accumulator. That bet simply does not increase the total settlement multiplier.

Final Winning State

The third bet wins:

bet statuses = [1, 3, 1]

Settlement multipliers:

first bet: 1.80
second bet: 1
third bet: 2.00

Result:

calculate_coef = 1.80 × 1 × 2.00 = 3.60
real_win = 10 × 3.60 = 36
coupon status = 2

Final callback:

{
  "event": "coupons.settled",
  "batchId": "batch-version-003",
  "clientId": 17,
  "couponCount": 1,
  "coupons": [
    {
      "coupon_code": "000000000350",
      "realWin": 36,
      "calculate_coefficient": 3.6,
      "status": 2,
      "calculate_date": 1784980800000,
      "events_data": [
        {
          "uuid": "912",
          "status": 1,
          "calculate_coefficient": 1.8,
          "calculate_date": 1784973600000,
          "calculate_score": "2:1",
          "timer": 0
        },
        {
          "uuid": "913",
          "status": 3,
          "calculate_coefficient": 1,
          "calculate_date": 1784977200000,
          "calculate_score": "0:0",
          "timer": 0
        },
        {
          "uuid": "914",
          "status": 1,
          "calculate_coefficient": 2,
          "calculate_date": 1784980800000,
          "calculate_score": "4:0",
          "timer": 0
        }
      ]
    }
  ]
}

After verification and storage, the partner credits the user once:

realWin = 36

Use the value returned in the callback, or real_win from the full API. The formula above explains the result, but must not replace the value calculated by the API.

Separate Branch: First Loss

Consider another accumulator:

[1, 0, 0] → first bet won
[1, 2, 0] → second bet lost

As soon as a bet receives status 2:

coupon status = 4
realWin = 0

The system immediately sends a loss callback even if the third bet is not settled yet.

The partner:

  1. updates the coupon to status 4;
  2. records the financial result as a loss;
  3. does not make a credit;
  4. records that the result has already been processed financially;
  5. returns HTTP 200.

Settlement of the remaining bets continues, but separate intermediate callbacks are not sent after the first loss.

When all bets have been settled, a full snapshot arrives:

[1, 2, 1]
coupon status = 4
realWin = 0
new batchId

This is not a second loss or a new financial operation. Update the remaining bet statuses, but do not process the balance again.

Multiple Versions and Deduplication

For one accumulator:

Situationcoupon_codebatchIdAction
New intermediate stateSameNewUpdate the coupon and bets.
Final resultSameNewUpdate the data and process the payout once.
Repeat of the same HTTP deliverySameSameDo not repeat business operations; return 200.

Do not treat every new batchId as a new bet or a new financial operation.

Store:

coupon_code → coupon identifier
uuid        → bet identifier within the callback
batchId     → delivery version identifier

Half Results in an Accumulator

Half Win

Two bets:

WIN:      coef = 1.80, factor = 1.80
HALF_WIN: coef = 2.00, factor = (2.00 + 1) / 2 = 1.50

For amount = 10:

calculate_coef = 1.80 × 1.50 = 2.70
real_win = 27

Half Loss

Two bets:

WIN:       coef = 1.80, factor = 1.80
HALF_LOSE: factor = 0.5

For amount = 10:

calculate_coef = 1.80 × 0.5 = 0.90
real_win = 9

HALF_LOSE does not reduce the accumulator to zero. A full loss with bet status 2 has a multiplier of 0 and reduces the entire coupon to zero.

Always use the final real_win returned by the API for crediting.

Retrieval Through the API

The complete current state is available by coupon code:

COUPON_CODE="000000000350"

curl --request GET \
  --url "$BASE_URL/api/partner/coupons/get?coupon_code=$COUPON_CODE" \
  --header "Accept: application/json" \
  --header "Authorization: Bearer $TOKEN"

When processing the response:

  • update the coupon by coupon_code;
  • iterate over the complete events_data array;
  • update bets by coupon_code + id;
  • do not treat status 0 as final;
  • use real_win only for a final state that has not yet been processed.

Fallback polling is recommended even when callbacks are working:

GET /api/partner/coupons/calculated?time=10

Return for Recalculation

If the accumulator is returned for recalculation:

coupon status = 15
one or more bets have status = 4

If the previous result was already processed financially, on first receiving this state:

  1. deduct the accumulator amount once again;
  2. move the coupon into a state awaiting a new result;
  3. do not treat status 15 as final;
  4. credit the new real_win after the next final state.

The repeated deduction is made once for each new transition to status 15, not once for every bet with status 4.

Final Algorithm

1. Verify that 2 to 15 different matches are selected.
2. Show the user one accumulator and one total amount.
3. Submit all pointers with multi = false.
4. With code = 0, do not create an accepted coupon or deduct the amount.
5. With code = 1, store one coupon and all events_data.
6. Deduct amount once.
7. Update the coupon and bets with every new state.
8. Do not credit funds while status = 0.
9. Process real_win for the first financially new final result.
10. Acknowledge a repeated batchId without repeated processing.
11. Perform fallback reconciliation through the API.

Checklist

  • The accumulator contains 2 to 15 bets.
  • Each bet belongs to a separate match.
  • A main event is not combined with any of its sub-events.
  • multi is false.
  • amount applies to the entire accumulator.
  • The user sees one total deduction amount.
  • The coupon is stored only after code = 1.
  • All bets from events_data are stored.
  • One initial deduction is made.
  • Every callback signature is verified against the raw request body.
  • Intermediate coupon status 0 does not trigger a payout.
  • A new batchId updates the existing coupon_code.
  • A repeated batchId does not repeat processing.
  • The first loss is processed immediately.
  • The final full snapshot of a losing accumulator does not create a second financial operation.
  • HALF_WIN, HALF_LOSE, return, and push are handled using the final real_win.
  • Status 15 starts one new result-waiting cycle.
  • Callbacks are supplemented with fallback polling.

More information:

Next section: Recovery After a Missed Callback.