For the complete documentation index, see llms.txt. This page is also available as Markdown.

User Manual

This guide is for external MBS Ticket 3 SDK clients that send requests to the MTS Gate Sandbox instead of a live MBS environment.

How you connect: use the Java MBS SDK to build a TicketRequest and send it over the WebSocket session the SDK opens for you.

The SDK serializes builders to Ticket 3.0 JSON. Download the request and response schemas from JSON Schemas. Sandbox routing still depends on specific endCustomer.id, event, and selection values called out in each scenario.

Covered scenarios:

Scenario
Detail page

single_accepted

accumulator_prematch

odds_format_accepted

odds_format_rejected

rejected_ticket_exception

rejected_odds_too_high

rejected_market_not_active

rejected_liability_limit

outright_not_active

single_live_async

alt_odds

alt_stake

reoffer

promo_stake

promo_boosted_odds

promo_payout_modifier

system_prematch

ways_prematch

custom_bet

external_bet

multi_bet

system_banker_ways

cancel_full_accepted

cancel_full_rejected

cancel_partial

cashout_full_accepted

cashout_full_rejected

cashout_partial

max_stake

ticket_inform

external_settlement

fallback

Each page has build steps, Java examples, wire JSON, field tables, expected replies, and common mistakes.


Using the Java MBS SDK

Package root: com.sportradar.mbs.sdk.

1

Configure and connect

Create MbsSdkConfig with the WebSocket and OAuth values Sportradar provides for your sandbox (or production) integration, then connect once and reuse the client:

Use 9985 as operatorId in sandbox examples (same value as Ticket 3.0 sample fixtures). The sandbox ignores operatorId for routing and reply building; it still must be present on the Ticket 3.0 envelope.

The SDK is thread-safe; one instance can serve the application lifespan. It handles OAuth and the WebSocket; you do not open the socket yourself.

Close the SDK

When the MbsSdk instance is no longer needed, tear it down so it can release the resources it holds (WebSocket connections, background workers, buffers):

Call close() during application shutdown (or when you permanently stop using this client). After close(), do not call sendTicket on that instance — you will get SdkNotConnectedException. Prefer a single long-lived client with one close() at the end over opening and closing per request.

2

What the SDK fills for you

When you call sendTicket, the SDK wraps your TicketRequest in the Ticket 3.0 envelope:

Envelope field
Who sets it

operation

SDK → "ticket-placement"

version

SDK → "3.0"

operatorId

From MbsSdkConfig (9985 in examples; sandbox ignores it for routing)

correlationId

Generated by the SDK

timestampUtc

Set by the SDK

content

Your TicketRequest (JSON type "ticket")

You only build the ticket content with builders (ticketId, context, bets).

3

Build the request

Purpose
Java API

Ticket / context / end customer

TicketRequest.newBuilder(), TicketContext.newBuilder(), EndCustomer.newBuilder().setId(...)

Channel (example)

Channel.newMobileChannelBuilder()

Bet / bet context

Bet.newBuilder(), BetContext.newBuilder().setOddsChange(OddsChange.HIGHER)

Ticket ref

TicketRef.newAltStakeTicketRefBuilder(), TicketRef.newReofferTicketRefBuilder()

UF / accumulator / system / ways

Selection.newUfSelectionBuilder(), newAccumulatorSelectionBuilder(), newSystemSelectionBuilder(), newWaysSelectionBuilder()

Custom bet / external / odds-boost / payout-modifier

newUfCustomBetSelectionBuilder(), newExtSelectionBuilder(), newOddsBoostSelectionBuilder(), newPayoutModifierSelectionBuilder()

Odds formats

Odds.newDecimalOddsBuilder(), newFractionalOddsBuilder(), newMoneylineOddsBuilder(), newHongKongOddsBuilder(), newIndonesianOddsBuilder(), newMalayOddsBuilder()

Stakes

Stake.newCashStakeBuilder(), newBonusStakeBuilder(), newFreeStakeBuilder()

Cancel / cashout / max-stake / inform / ext-settlement

CancelRequest, CashoutInformRequest / CashoutRequest, MaxStakeRequest, TicketInformRequest, ExtSettlementRequest

Send

getTicketProtocol().sendTicket / sendCancel / sendCashoutInform / sendCashout / sendMaxStake / sendTicketInform / sendExtSettlement

Reply

Matching *Response (getStatus(), getCode(), getMessage(), …)

Builders do not validate required fields locally. Missing Ticket 3.0 / sandbox fields fail at the server or route to the wrong scenario.

4

Send and read the reply

TicketResponse getter

Meaning in these scenarios

getStatus()

AcceptanceStatus.ACCEPTED (wire: "accepted")

getCode()

0

getMessage()

"accepted" (sandbox)

getTicketId()

Echo of your request ticket id

getSignature()

Sandbox signature string

getBetDetails()

Per-bet / selection details (selection echoed)

Shared sandbox constants

Purpose
Value
Set via

Prematch match (typical first event)

sr:match:15050881

First UF / nested UF eventId

Outright event

sr:season:55413

UF eventId for outright reject

External event

ext:match:39999

ExtSelection.setEvent

Prematch / live product id

"3" / "1"

UfSelection.setProductId

Placement customers (examples)

customer, customer_rejected_*, customer_alt_*, customer_promo, customer_live_async_accepted, customer_reoffer_*, customer_accumulator, customer_system, customer_ways, customer_custom_bet, customer_external, customer_multi_bet, customer_system_banker, customer_inform

EndCustomer.setId

Cancel / cashout customers

customer_cancel_*, customer_cashout_*

Transaction meta.punter.id or magic ticketId

Magic cancel ticket ids

mock_cancel_accepted, mock_cancel_rejected, mock_cancel_partial

CancelDetails ticket id

Magic cashout ticket ids

mock_cashout_full, mock_cashout_rejected, mock_cashout_partial

Cashout details ticket id

Max-stake amounts (sandbox)

2872885 EUR (UF) / 1950000 EUR (accumulator)

Reply stake amount

Routing facets

Facet
SDK field
Role

End customer id

TicketContextEndCustomer.id (placement / inform)

Selects scenario family

Event id

First selection event (UF / nested / external)

Usually sr:match:15050881; outright / external use their own ids

Odds change

BetContext.oddsChange

Required higher for alt_odds

Ref type

TicketContext.ref (alt-stake / reoffer)

Alt-stake step 2 and reoffer routes

Stake type

First Stake subtype

cash → facet cash; bonus; free → facet freebet

Selection type

Top-level Selection subtype (or multi-bet / system-banker shape)

uf, accumulator, system, ways, custom-bet, external, odds-boost, payout-modifier, …

Odds format

Selection Odds subtype

decimal, fractional, american (moneyline), hong_kong, indonesian, malay

Cancel / cashout

Full vs partial + percentage + punter / magic ticket id

See cancel_* / cashout_* pages

Ticket-inform validation code

betValidations[0].code

Must be 3 for ticket_inform


How routing works

The sandbox does not validate operators or call upstream MTS. It derives routing facets from your ticket and picks a deterministic scenario.

Schema/ticket content checklist

Even though the SDK fills the envelope, the ticket body must still satisfy Ticket 3.0:

Field
Rules

ticketId

Required string (1–128 chars)

context.limitId

Required integer ≥ 1 (client limit id from Sportradar)

context.channel

Required (e.g. mobile + lang)

context.endCustomer.id

Required for sandbox routing here; 1–36 chars: [0-9A-Za-z#\-_]

Each UF selection

productId, eventId, marketId, outcomeId, odds

Each cash stake

amount, currency


Error responses

For placement scenarios, the failure you will almost always see when routing misses is a rejected ticket reply, not a separate Ticket 3.0 error-reply. Cancel / cashout / max-stake / inform / ext-settlement miss the same way (code −999 on their reply types) — see fallback.

1

Rejected ticket reply (no matching scenario) — primary sandbox failure

When the ticket does not match a sandbox route (wrong endCustomer.id, event, selection type, odds format, etc.), the sandbox returns a ticket-reply with rejected status and code -999.

Message text in this codebase:

No matching sandbox scenario found

(There is no string like “could not route to a valid Scenario” in the sandbox.)

Field
Value

content.type

ticket-reply

content.status

rejected

content.code

-999

content.message

No matching sandbox scenario found

content.ticketId

Echo of your request ticket id

content.signature

sandbox-signature

Java SDK: sandbox outcomes arrive as a normal TicketResponse. Handle them with if on the response (not try-catch). Use try-catch for internal SDK/transport failures.

Example wire JSON:

Typical causes of a routing miss:

Cause
Example

Wrong end customer

customer vs customer_accumulator swapped

Wrong first event

Not sr:match:15050881

Wrong selection type

UF when you meant accumulator (or the reverse)

Wrong odds format

Non-decimal odds while expecting these accepted routes

2

Schema validation error (narrow case)

The sandbox does implement an internal RESP_ERROR_REPLY path, but only when placement has more than one bet, or more than one top-level selection on the first bet (and the customer is not customer_multi_bet / customer_system_banker). That path is not used for “unknown scenario” routing.

Internal field
Value

Response content type

RESP_ERROR_REPLY

error.errorCode

400

error.errorMessage

The mts_sandbox currently only supports single-bet, single-selection tickets.

If the gateway in front of the sandbox delivers that as Ticket 3.0 error-reply, the Java SDK does not return a TicketResponse. It fails the call with ServerErrorResponseException (as ExecutionException.getCause() when using blocking sendTicket):

This is not ProtocolInvalidRequestException — that exception is for protocol “request not processed” cases, not for a deserialized error-reply. For wrong routing keys, expect the rejected TicketResponse above, not this path.

Notes for these scenarios:

  • A single top-level accumulator with nested UF legs is valid (one top-level selection).

  • Putting two UF selections as siblings on the same bet (without an accumulator wrapper) hits this schema validation for ordinary customers. Only customer_multi_bet (multiple bets) and customer_system_banker (multiple top-level selections) are exempt.

How to tell the failures apart

No matching scenario (common)
Schema validation failure (rare here)

Sandbox path

fallback_scenario

sandbox_api_error_response

Meaning

Ticket OK enough to route, but no table row matched

Multi-bet / multi top-level selection rejected before routing

Typical result for SDK users

TicketResponse with AcceptanceStatus.REJECTED, code -999, message No matching sandbox scenario found — handle with if

If exposed as Ticket 3.0 error-reply: ServerErrorResponseException (getErrorCode() / getMessage()) — handle in catch

Handling sandbox vs SDK errors

Keep these two layers separate:

Layer
How it surfaces
How you handle it

Sandbox (accepted / rejected ticket)

TicketResponse returned successfully

if on getStatus(), getCode(), getMessage()

SDK / transport

Exception (usually wrapped in ExecutionException from blocking sendTicket)

try / catch

sendTicket is blocking and declared to throw ExecutionException and InterruptedException. SDK failures complete the async call exceptionally, so with the blocking API they appear as ExecutionException.getCause().

Exception
When

SdkNotConnectedException

sendTicket before connect(), or after close() / dispose

ProtocolTimeoutException

No matching WebSocket reply within the configured timeout

ServerErrorResponseException

Server returned Ticket 3.0 error-reply (SDK maps it to this exception; includes sandbox schema-validation if delivered as error-reply)

ProtocolInvalidRequestException

Protocol layer reported the request was not processed (distinct from error-reply)

ProtocolSendFailedException

Send over the WebSocket failed

Always call mbsSdk.connect() before sendTicket, and keep the session open while waiting for the reply. Do not use try-catch to detect sandbox rejection (-999); that is a successful protocol round-trip with a rejected TicketResponse.


Quick reference

Scenario
Customer / key
Builder / operation highlight
Outcome
Detail

single_accepted

customer + decimal

newUfSelectionBuilder

ACCEPTED

odds_format_accepted

customer + non-decimal odds

fractional / moneyline / HK / indonesian / malay

ACCEPTED

odds_format_rejected

customer_rejected_odds + non-decimal

same formats (no malay reject)

REJECTED −421

rejected_ticket_exception

customer_rejected_2xx

product 55, outcome ~1

−205

rejected_odds_too_high

customer_rejected_4xx

elevated decimal odds

−421

rejected_market_not_active

customer_rejected_5xx

normal UF

−506

rejected_liability_limit

customer_rejected_7xx

high cash stake

−703

outright_not_active

customer_rejected_outright

event sr:season:55413

−407

single_live_async

customer_live_async_accepted

live product "1"

code 202 / PENDING

alt_odds

customer_alt_odds

OddsChange.HIGHER

ACCEPTED + auto odds 7.3

alt_stake

customer_alt_stake

no ref → −713+suggestion; alt-stake ref → ACCEPTED

see page

reoffer

customer_reoffer_rejected / _accepted

reoffer ref

−430 then ACCEPTED

promo_*

customer_promo

bonus/free stake or odds-boost / payout-modifier

ACCEPTED

accumulator_prematch

customer_accumulator

accumulator

ACCEPTED

system_prematch / ways_prematch / custom_bet / external_bet

matching customer_*

system / ways / uf-custom-bet / external

ACCEPTED

multi_bet / system_banker_ways

customer_multi_bet / customer_system_banker

multi-bet / multi top-level selection

ACCEPTED

Cancel / cashout

customer_cancel_* / customer_cashout_* or magic ids

sendCancel / sendCashoutInform

accepted or coded reject

max_stake

(omit customer)

sendMaxStake

2872885 / 1950000 EUR

ticket_inform

customer_inform + validation code 3

sendTicketInform

ACCEPTED

external_settlement

customer_external (punter)

sendExtSettlement, zero cash payout

ACCEPTED

(no route match)

unmatched combination

rejected, code −999, No matching sandbox scenario found

Last updated

Was this helpful?