perform-transaction
The perform-transaction endpoint is the core of wallet integration. It handles all financial operations including deposits, withdrawals, and rollbacks for bets, settlements, and bonus operations.
/api/v2/wallet/{playerId}/transactionsKey points:
- Must guarantee idempotency (same transaction ID = same response)
- Get balance is called after each transaction by default
See Integration for the rules shared by all three wallet endpoints.
Quick Reference
Transaction Types Overview
| Type | Direction | Required | Common Use Cases |
|---|---|---|---|
withdrawal | Money out | Yes | Bets, cancel settlements, negative adjustments |
deposit | Money in | Yes | Settlements, winnings, positive adjustments |
rollback | Reverse | Yes | Bet placement cancellations only |
Bonus Integration
Transaction types award, release, and retract are only required if you integrate with the GR8 Tech Bonus System.
Endpoint Configuration
Request Timeout: 2 seconds All transaction requests have a 2-second timeout. Your endpoint must respond within this time limit, otherwise:
- Retry logic will be triggered (see Rollback and Retry Logic)
- For bet placement, a rollback will be initiated
Ensure your wallet system can process requests quickly and efficiently.
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
| playerId | Path | string | Yes | Unique player identifier |
| X-Brand | Header | string | Yes | Tenant identifier representing site and operator (configured during setup, cannot be changed). Get this value from your GR8 Tech manager. |
| X-Operator-Id | Header | string | Yes | Tenant identifier representing site and operator (configured during setup, cannot be changed). Get this value from your GR8 Tech manager. |
Request Structure
Core Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique transaction identifier. Used for idempotency - same ID = same result |
| currency | string | Yes | ISO 4217 currency code (USD, EUR, KES, etc.) |
| platform | string | Yes | Platform identifier (typically “sport” or “CRM”) |
| type | string | Yes | Transaction type: deposit, withdrawal, rollback, award, release, retract |
| initiatedAt | string | Yes | ISO 8601 timestamp when transaction was initiated |
| createdAt | string | Yes | ISO 8601 timestamp when transaction was created |
Context Object
| Field | Type | Required | Description |
|---|---|---|---|
| product | string | Yes | Product identifier (sportsbook, crm) |
| reason | string | Yes | Transaction reason code (see Reference) |
| channel | string | No | Bet related channel for bet transaction (placement + settlement) |
| betId | string | No | Bet identifier (present for bet-related transactions) |
| parentId | string | No | Parent transaction ID. Sent only on rollback transactions, where it always points to the bet placement being reversed. |
| sessionId | string | No | Player session identifier. Present in bet placement transactions when the operator passes a sessionId when starting the session — in an iFrame integration, as a query parameter of the iFrame URL |
| language | string | No | Player language at the moment of bet placement (taken from the X-Language header of the placement request). Sent only on bet placement transactions — reason = bet, bet with tax, or freebet. Use it to localize the error message when rejecting a bet with decline.client.custom. |
| SportBonusOfferId | string | No | Activity ID from Journey Builder (for bonus transactions) |
| SportBonusPlayerOfferId | string | No | Player-specific bonus activation ID (for bonus transactions) |
| extras | object | No | Extended bet details — bet type, total odds, and per-item event, market and outcome data. Disabled by default, enabled per brand on request. See Extended Bet Details |
Amount Breakdown
| Field | Type | Description |
|---|---|---|
| cash | string | Cash amount as decimal string (e.g., “100.50”) |
| bonus | string | Bonus amount as decimal string |
| locked | string | Locked amount as decimal string (used for wagering requirements) |
| retract | string | Retract amount as decimal string (bonus funds removed/excess) |
Request Schema
Full Request Schema (JSON Schema)
{
"type": "object",
"properties": {
"currency": {
"type": "string",
"description": "ISO 4217 currency code (e.g., USD, EUR, KES)"
},
"platform": {
"type": "string",
"description": "Platform identifier (e.g., sport, CRM)"
},
"id": {
"type": "string",
"description": "Unique transaction identifier (used for idempotency)"
},
"type": {
"type": "string",
"enum": ["deposit", "withdrawal", "rollback", "award", "release", "retract"],
"description": "Transaction type"
},
"initiatedAt": {
"type": "string",
"format": "date-time",
"description": "When transaction was initiated (ISO 8601)"
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "When transaction was created (ISO 8601)"
},
"context": {
"type": "object",
"properties": {
"channel": {
"type": "string",
"description": "Bet related channel for bet transaction (placement + settlement)"
},
"product": {
"type": "string",
"description": "Product identifier (e.g., sportsbook, crm)"
},
"reason": {
"type": "string",
"description": "Transaction reason (see Transaction Types Reference section)",
},
"betId": {
"type": "string",
"description": "Bet identifier (for bet-related transactions: bet, settle, resettle...)"
},
"parentId": {
"type": "string",
"description": "Parent transaction ID (for rollbacks)"
},
"SportBonusOfferId": {
"type": "string",
"description": "Bonus offer ID (Activity ID from Journey Builder)"
},
"SportBonusPlayerOfferId": {
"type": "string",
"description": "Player-specific bonus activation ID"
},
"sessionId": {
"type": "string",
"description": "Player session identifier. Present in bet placement transactions when the operator passes a sessionId when starting the session (in an iFrame integration, as a query parameter of the iFrame URL)"
},
"language": {
"type": "string",
"description": "Player language at the moment of bet placement (taken from the X-Language header of the placement request). Sent only on bet placement transactions (reason = bet, bet with tax, or freebet). Use it to localize the error message when rejecting a bet with decline.client.custom."
},
"extras": {
"type": "object",
"description": "Extended bet details. Disabled by default; enabled per brand on request. See the Extended Bet Details page for the full schema."
}
},
"required": ["product", "reason"],
"additionalProperties": true
},
"amountBreakdown": {
"type": "object",
"properties": {
"cash": {
"type": "string",
"description": "Cash amount (decimal string)"
},
"bonus": {
"type": "string",
"description": "Bonus amount (decimal string)"
},
"locked": {
"type": "string",
"description": "Locked amount (decimal string)"
},
"retract": {
"type": "string",
"description": "Retract amount (decimal string)"
}
},
"additionalProperties": true
}
},
"required": ["currency", "platform", "id", "type", "amountBreakdown", "initiatedAt", "createdAt", "context"]
}Basic Request Example
{
"id": "5f338453-6172-491f-b8f0-27e382ea05eb",
"currency": "USD",
"platform": "sport",
"type": "withdrawal",
"createdAt": "2025-01-29T00:34:25Z",
"initiatedAt": "2025-01-29T00:34:25Z",
"context": {
"channel": "DESKTOP_AIR_PM",
"product": "sportsbook",
"reason": "bet",
"betId": "5f338453-6172-491f-b8f0-27e382ea05eb",
"sessionId": "some-session-id"
},
"amountBreakdown": {
"cash": "13.9",
"locked": "0",
"bonus": "0"
}
}For more examples, see Transaction Examples.
Response Structure
Idempotency Requirements Your wallet MUST implement idempotency to handle duplicate transaction requests:
- Store transaction snapshots for at least 3 days
- Return the same response for duplicate transaction IDs (same
idfield) - Set
alreadyProcessed: truefor duplicate requests - Store snapshots even for failed transactions (e.g.,
decline.lowbalance)
This ensures system reliability during network issues and retries.
Success Response
All successful transactions return HTTP 200 with the following structure:
Full Response Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"currency": {
"type": "string"
},
"platform": {
"type": "string"
},
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": ["deposit", "withdrawal", "rollback", "award", "release", "retract"]
},
"balances": {
"type": "object",
"description": "Nested structure: platform -> product -> currency -> balance details",
"patternProperties": {
"^.*$": {
"type": "object",
"patternProperties": {
"^.*$": {
"type": "object",
"patternProperties": {
"^.*$": {
"type": "object",
"properties": {
"cash": {
"type": "string"
},
"bonus": {
"type": "string"
},
"locked": {
"type": "string"
},
"retract": {
"type": "string"
}
},
"required": ["cash", "bonus", "locked"],
"additionalProperties": false
}
}
}
}
}
}
},
"alreadyProcessed": {
"type": "boolean",
"description": "True if transaction was already processed (idempotency)"
},
"initiatedAt": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"context": {
"type": "object",
"properties": {
"product": {
"type": "string"
},
"reason": {
"type": "string"
}
},
"additionalProperties": true
},
"amountBreakdown": {
"type": "object",
"properties": {
"cash": {
"type": "string"
},
"bonus": {
"type": "string"
},
"locked": {
"type": "string"
},
"retract": {
"type": "string"
}
},
"additionalProperties": false
}
},
"required": [
"currency",
"platform",
"id",
"type",
"balances",
"alreadyProcessed",
"initiatedAt",
"createdAt",
"context",
"amountBreakdown"
]
}Response Examples
First Request (New Transaction)
{
"balances": {
"sport": {
"main": {
"USD": {
"cash": "86.1",
"bonus": "0",
"locked": "0",
"retract": "0"
}
},
"sportsbook": {
"USD": {
"cash": "86.1",
"bonus": "0",
"locked": "0",
"retract": "0"
}
}
}
},
"alreadyProcessed": false,
"currency": "USD",
"platform": "sport",
"id": "5f338453-6172-491f-b8f0-27e382ea05eb",
"type": "withdrawal",
"initiatedAt": "2025-01-29T00:34:25.000Z",
"createdAt": "2025-01-29T00:34:26.000Z",
"context": {
"channel": "DESKTOP_AIR_PM",
"product": "sportsbook",
"reason": "bet"
},
"amountBreakdown": {
"cash": "13.9",
"bonus": "0",
"locked": "0"
}
}Duplicate Request (Idempotency)
If the same transaction ID is sent again, return the exact same response with alreadyProcessed: true:
{
"balances": {
"sport": {
"main": {
"USD": {
"cash": "86.1",
"bonus": "0",
"locked": "0",
"retract": "0"
}
},
"sportsbook": {
"USD": {
"cash": "86.1",
"bonus": "0",
"locked": "0",
"retract": "0"
}
}
}
},
"alreadyProcessed": true,
"currency": "USD",
"platform": "sport",
"id": "5f338453-6172-491f-b8f0-27e382ea05eb",
"type": "withdrawal",
"initiatedAt": "2025-01-29T00:34:25.000Z",
"createdAt": "2025-01-29T00:34:26.000Z",
"context": {
"channel": "DESKTOP_AIR_PM",
"product": "sportsbook",
"reason": "bet"
},
"amountBreakdown": {
"cash": "13.9",
"bonus": "0",
"locked": "0"
}
}For more response examples, see Transaction Examples.
Error Responses
Error Response Structure
Return HTTP 400 with the following structure for business logic errors:
Error Response Schema
{
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Error code from the error codes list"
},
"message": {
"type": "string",
"description": "Human-readable error message (English, for logs/operations)"
},
"origin": {
"type": "string",
"description": "Your company/system name"
},
"details": {
"type": "object",
"description": "Optional structured payload for the error.",
"properties": {
"localizedMessage": {
"type": "string",
"description": "Player-language message to be displayed to the player as-is. Used together with the decline.client.custom error code; the language is supplied to your wallet via context.language on the original placement request."
}
},
"additionalProperties": true
}
},
"required": ["code", "message", "origin"]
},
"alreadyProcessed": {
"type": "boolean",
"description": "Optional: True if this error was already returned for this transaction ID"
},
"balances": {
"type": "object",
"description": "Optional. Current player balances — same nested structure (platform -> product -> currency -> balance details) as in the success response. When present on a decline.lowbalance error, the Sportsbook BE will use these balances directly instead of making a follow-up get-user-balance call. See Returning balances in error responses.",
"patternProperties": {
"^.*$": {
"type": "object",
"patternProperties": {
"^.*$": {
"type": "object",
"patternProperties": {
"^.*$": {
"type": "object",
"properties": {
"cash": { "type": "string" },
"bonus": { "type": "string" },
"locked": { "type": "string" },
"retract": { "type": "string" }
},
"required": ["cash", "bonus", "locked"],
"additionalProperties": false
}
}
}
}
}
}
}
},
"required": ["error"]
}Error Response Examples
New Error
{
"error": {
"code": "decline.lowbalance",
"message": "Not enough funds for performing transaction",
"origin": "YourCompanyName"
},
"alreadyProcessed": false
}Duplicate Error (Idempotency)
If the same transaction ID is sent again and previously failed, return the same error with alreadyProcessed: true:
{
"error": {
"code": "decline.lowbalance",
"message": "Not enough funds for performing transaction",
"origin": "YourCompanyName"
},
"alreadyProcessed": true
}Returning balances in error responses
Your wallet may include the player’s current balances in any error response — the field has the exact same nested structure (platform → product → currency → balance details) as in a successful response.
Primary use case — decline.lowbalance. By default, after a decline.lowbalance the Sportsbook BE calls get-user-balance to show the player the amount available for placing a bet. If your error response already carries balances, the Sportsbook BE uses them directly and skips the follow-up call — fewer round-trips and less load on your wallet.
Behavior notes:
- The field is optional. If omitted, the existing behaviour (extra
get-user-balancecall) is preserved. - Including
balancesis also supported on other errors, butdecline.lowbalanceis the case where it provides a measurable benefit, since it’s the one that triggers an automatic balance refresh. - Idempotency rules still apply: when returning the same error for a duplicate transaction ID (
alreadyProcessed: true), return the samebalancesyou returned originally.
See Error Response with balances for the full example.
Common Error Codes
Every code your wallet may return, with its HTTP status and when it applies, is listed on Wallet Error Codes.
Custom decline with localized message
Use decline.client.custom when your wallet wants to reject a bet for an operator-specific reason and display a tailored message to the player. The Sportsbook surfaces the message as-is — you do not need to register the reason on our side and we do not translate or rewrite it.
Two parts work together:
- Inbound —
context.language. On bet placement transactions (reason=bet,bet with tax, orfreebet), the requestcontextcarries the player’s current language (e.g.en,uk,es). Use it to pick the correct localization forlocalizedMessage. - Outbound —
error.details.localizedMessage. Reject with HTTP 400 and the standard error envelope, but populatedetails.localizedMessagewith the text the player should see. The Sportsbook returns this exact string to the frontend as the error reason; the top-levelerror.messageis used only for logs/operations and is never displayed.
Behavior notes:
- This applies only to bet placement; settlements, rollbacks and bonus transactions are not user-facing in the same way and do not need a localized message.
- If
context.languageis missing or your wallet has no translation for it, fall back to a default language localizedMessageis the only field we read fromdetailsfor this purpose; you may include other diagnostic fields alongside it, they will be ignored by the Sportsbook.- Idempotency rules apply as for any other declined transaction: store the snapshot and return the same response (with
alreadyProcessed: true) on a duplicate transaction ID.
See Error Response with Localized Message for the full request/response example.
Rollback and Retry Requirements
Critical: Must Read Rollback handling has special requirements including:
- Applies to bet placements only — a settlement is reverted with
cancelsettle, not with a rollback - Specific error codes (
decline.parent.notfound,decline.parent.failed) - 400 retries (5 min delay) until a definitive response
- High availability requirements
Read the complete guide: Rollback and Retry Logic
Implementation Checklist
Core Integration (Required)
- Implement endpoint with your URL structure
- Handle core transaction types:
deposit,withdrawal,rollback - Implement idempotency check (same
id= same response) - Store transaction snapshots for at least 3 days (including failed transactions)
- Return correct balance structure in response
- Handle core reason codes: bet, settle, resettle, cancelsettle, sport rollback
- Route
sport rollbackandcancelsettlethrough separate handlers — a rollback reverses a placement and validatesparentId, a cancel settle reverses a settlement and has no parent - Implement proper error responses with correct error codes
- ⚠️ CRITICAL: Read and implement Rollback and Retry Logic
- Test retry scenarios for each operation type
- Configure
X-BrandandX-Operator-Idheader handling - Notify GR8 Tech of your endpoint URL
- Test with GR8 Tech team before going live
Bonus Integration (Optional)
Only required if integrating with GR8 Tech Bonus System:
- Handle bonus transaction types:
award,release,retract - Implement bonus balance tracking (bonus, locked, retract)
- Handle bonus reason codes: bonus award, bonus release, bonus limit release
- Handle freebet reason codes: freebet award, freebet, settle freebet, etc.
- Test bonus award and release flows
- Test freebet flows (if applicable)
- Review Sport Bonus Amount documentation
- Review Freebet documentation (if applicable)
Related Documentation
Core Wallet Integration
- Transaction Types Reference - Complete type and reason mapping
- Transaction Examples - Request/response examples for all types
- Extended Bet Details - Optional
context.extraspayload describing the bet in full - Rollback and Retry Logic - Critical rollback requirements and retry behavior
- Get Balance - Retrieve player balance
- Wallet Error Codes - Complete error code reference
Bonus System Integration (Optional)
- General Bonus Information - Bonus system overview and setup
- Sport Bonus Amount - Wagering bonus details
- Freebet - Freebet transaction flows
- Cash Bonus - Direct cash awards