Skip to main content

Callback Specification

This page documents the four HTTP endpoints that your server must implement to support Seamless Wallet integration. Ruby calls these endpoints in real time for every game event.


Overview

All four endpoints share the following properties:

  • Method: POST
  • Base URL: your configured callback_url
  • Content-Type: application/json (request and response)
  • Authentication: HMAC-SHA256 signature headers (see Callback Authentication)
  • Timeout: 5 seconds hard cutoff; target < 3 seconds
  • Success criteria: HTTP 2xx plus the valid JSON response contract below
  • Failure behavior: non-2xx, timeout, oversized body, malformed JSON, or an invalid response field → immediate error, no application-level automatic retry

URL Construction

Ruby strips any trailing slash from your callback_url before appending the path:

callback_url = "https://wallet.example.com/ruby"
→ POST https://wallet.example.com/ruby/balance
→ POST https://wallet.example.com/ruby/debit
→ POST https://wallet.example.com/ruby/credit
→ POST https://wallet.example.com/ruby/rollback

The URL must be absolute HTTPS and must not contain credentials, a query string, or a fragment. A trailing slash is accepted and removed when the Brand is saved.

Successful Response Contract

Every successful body must be an uncompressed JSON object no larger than 64 KiB; Ruby sends Accept-Encoding: identity and rejects any response with a compressed Content-Encoding. Every required balance or balance_before field is a strict, non-negative DECIMAL(18,2) strings: up to 16 integer digits, with an optional one- or two-digit fractional part. JSON numbers, signs, exponents, NaN, Infinity, missing fields, and out-of-range values are rejected.


POST /balance

Query a player's current balance. This is a stateless read — no funds are moved and no side effects are expected.

Request

{
"player_id": 12345,
"username": "player_handle",
"external_player_id": "brand-player-guid-001",
"provider_code": "evo"
}
FieldTypeRequiredDescription
player_idintegerYesRuby's internal player identifier
usernamestringNoPlayer's display name, if available
external_player_idstringNoYour stable player identifier, if supplied during launch
provider_codestringYesGame provider code (e.g., "evo", "pg")

Response

{
"balance": "1250.00"
}
FieldTypeDescription
balancestringCurrent player balance as a decimal string (e.g., "1250.00")

Notes

  • balance is a stateless query. There is no transaction_id and no idempotency requirement.
  • Always return the current real-time balance for the player.

POST /debit

Deduct funds from a player's wallet. Called when a player places a bet.

Request

{
"player_id": 12345,
"amount": "100.50",
"transaction_id": "txn_bet_abc123",
"username": "player_handle",
"external_player_id": "brand-player-guid-001",
"provider_code": "evo",
"round_id": "round_xyz",
"game_code": "baccarat_classic",
"ref_id": "external_ref_001",
"memo": "Bet on Baccarat round 7"
}
FieldTypeRequiredDescription
player_idintegerYesRuby's internal player identifier
amountstringYesAmount to deduct, as a decimal string (e.g., "100.50")
transaction_idstringYesUnique transaction identifier — use this for idempotency
usernamestringNoPlayer's display name
external_player_idstringNoYour stable player identifier, if supplied during launch
provider_codestringYesGame provider code
round_idstringNoGame round identifier
game_codestringNoGame identifier within the provider
ref_idstringNoExternal reference ID from the game provider
memostringNoHuman-readable description of the transaction

Response

{
"balance": "1149.50",
"balance_before": "1250.00"
}
FieldTypeDescription
balancestringPlayer's balance after the debit
balance_beforestringPlayer's balance before the debit

Idempotency

If you receive a POST /debit request with a transaction_id you have already successfully processed, return the same response as the original call. Do not deduct funds again.

If the original transaction is still in progress (e.g., due to a race condition), serialize access to prevent double-debit.

Error Cases

ScenarioRecommended HTTP Status
Insufficient funds402 Payment Required or 422 Unprocessable Entity
Player not found404 Not Found
Player account suspended403 Forbidden
Internal server error500 Internal Server Error

Any non-2xx response is treated as a failure by Ruby.


POST /credit

Add funds to a player's wallet. Called when a player wins or a settlement occurs.

Request

{
"player_id": 12345,
"amount": "200.00",
"transaction_id": "txn_win_def456",
"username": "player_handle",
"external_player_id": "brand-player-guid-001",
"provider_code": "evo",
"round_id": "round_xyz",
"game_code": "baccarat_classic",
"bet_amount": "100.50",
"original_transaction_id": "txn_bet_abc123",
"ref_id": "external_ref_002",
"memo": "Win payout for Baccarat round 7"
}
FieldTypeRequiredDescription
player_idintegerYesRuby's internal player identifier
amountstringYesAmount to credit, as a decimal string
transaction_idstringYesUnique transaction identifier — use this for idempotency
usernamestringNoPlayer's display name
external_player_idstringNoYour stable player identifier, if supplied during launch
provider_codestringYesGame provider code
round_idstringNoGame round identifier
game_codestringNoGame identifier within the provider
bet_amountstringNoOriginal stake amount when this credit settles a prior debit
original_transaction_idstringNoTransaction ID of the original debit/reserve being settled or refunded
standalone_creditbooleanNoPresent as true when the credit has no matching debit, such as a provider bonus; omitted otherwise
ref_idstringNoExternal reference ID from the game provider
memostringNoHuman-readable description of the transaction

Response

{
"balance": "1349.50",
"balance_before": "1149.50"
}
FieldTypeDescription
balancestringPlayer's balance after the credit
balance_beforestringPlayer's balance before the credit

Idempotency

If you receive a POST /credit request with a transaction_id you have already successfully processed, return the same response as the original call. Do not credit funds again.


POST /rollback

Reverse a prior debit transaction. Called when a round is voided or cancelled after a bet was already placed.

Request

{
"player_id": 12345,
"amount": "100.50",
"transaction_id": "txn_rollback_ghi789",
"username": "player_handle",
"external_player_id": "brand-player-guid-001",
"provider_code": "evo",
"round_id": "round_xyz",
"ref_id": "external_ref_003"
}
FieldTypeRequiredDescription
player_idintegerYesRuby's internal player identifier
amountstringYesAmount to restore, as a decimal string
transaction_idstringYesUnique transaction identifier for this rollback — use this for idempotency
usernamestringNoPlayer's display name
external_player_idstringNoYour stable player identifier, if supplied during launch
provider_codestringYesGame provider code
round_idstringNoGame round identifier
ref_idstringNoExternal reference ID from the game provider

Note: game_code and memo are not included in rollback requests.

Response

{
"balance": "1250.00"
}
FieldTypeDescription
balancestringPlayer's balance after the rollback

Note: balance_before is not included in the rollback response.

Idempotency

If you receive a POST /rollback request with a transaction_id you have already successfully processed, return the same response as the original call. Do not restore funds again.


Field Types Reference

FieldTypeNotes
player_idintegerAlways present in all four callbacks
amountstringDecimal representation, e.g. "100.50". Parse with Decimal or equivalent — do not use floating-point
balancestringDecimal representation of balance after transaction
balance_beforestringDecimal representation of balance before transaction (debit/credit only)
transaction_idstringOpaque unique string; use as a deduplication key
usernamestringMay be absent; do not depend on it for player lookup
external_player_idstringStable brand-side player identifier when supplied during launch; prefer it over username for wallet lookup
provider_codestringShort code identifying the game provider
round_idstringGame-level round identifier
game_codestringProvider-specific game identifier (not present in rollback)
bet_amountstringOriginal stake amount for settlement credits
original_transaction_idstringOriginal debit/reserve transaction ID linked to a settlement or refund credit
standalone_creditbooleanMarks credit-only events that have no prior debit
ref_idstringExternal reference from the game provider; may be absent
memostringHuman-readable note; may be absent (not present in rollback)

Idempotency Summary

Endpointtransaction_idIdempotency Required
/balanceNoNo — stateless read
/debitYesYes
/creditYesYes
/rollbackYesYes

Ruby records a successful response for each transaction_id. If Ruby calls you again with the same transaction_id (due to network uncertainty), your server must return the original result. Ruby does not rely on re-processing to be safe — but your server must defend against it independently.

Bind each key to the complete first request. An exact retry returns the stored result; reuse with a different player, provider, amount, or context must be rejected and alerted.


Retry Policy

There is currently no application-level automatic retry for failed callbacks. If a callback returns a non-2xx status code, is invalid, or times out (> 5 seconds), Ruby propagates the error immediately back to the game provider. A game provider may independently retry its original event; such a retry carries the same stable wallet transaction_id, so your idempotency guarantee remains mandatory.

Plan your error handling accordingly:

  • Return 5xx only for transient errors you expect to be retried manually
  • Return 4xx for permanent business errors (insufficient funds, player suspended)
  • Ensure your server can respond within 5 seconds under load