Perform Transaction¶
Overview¶
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.
Key points:
- You implement this endpoint with your own URL
- Notify GR8 Tech about your endpoint URL for configuration
- Must guarantee idempotency (same transaction ID = same response)
- Get balance is called after each transaction by default
On this page:
- Quick Reference
- Endpoint Configuration
- Request Structure
- Response Structure
- Error Responses
- Implementation Checklist
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 cancellations, reversals |
Bonus Integration
Transaction types award, release, and retract are only required if you integrate with the GR8 Tech Bonus System.
Quick Navigation¶
- Transaction Types Reference - Complete type and reason mapping
- Transaction Examples - Request/response examples for all types
- Wallet Error Codes - Error code reference
Endpoint Configuration¶
URL Format¶
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.
Multi-Tenant Setup
Each brand must use exactly one wallet endpoint. If you operate multiple brands, you can either:
- Provide different endpoint URLs per brand
- Use one endpoint URL with routing logic based on
X-BrandandX-Operator-Idheaders
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) |
| X-Operator-Id | Header | string | Yes | Tenant identifier representing site and operator (configured during setup, cannot be changed) |
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 (required for rollback transactions) |
| sessionId | string | No | Player session identifier. Present in bet placement transactions when the operator passes sessionId as a query parameter in the iFrame URL |
| language | string | No | Player language at the moment of bet placement (taken from the iFrame X-Language header). 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) |
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 sessionId as a query parameter in the iFrame URL"
},
"language": {
"type": "string",
"description": "Player language at the moment of bet placement (taken from the iFrame X-Language header). 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."
}
},
"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 in the iFrame 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 in the iFrame. 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¶
| Error Code | HTTP Status | Description |
|---|---|---|
| decline.lowbalance | 400 | Insufficient funds. May optionally include current balances in the response to skip the follow-up get-user-balance call (see Returning balances in error responses). |
| decline.parent.notfound | 400 | Parent transaction not found (for rollbacks) |
| decline.parent.failed | 400 | Parent transaction failed (for rollbacks) |
| error.user.not-found | 400 | Player not found |
| decline.player.blocked | 400 | Player account is blocked |
| decline.currency.mismatch | 400 | Currency mismatch |
| decline.client.custom | 400 | Operator-specific decline. The reason text is provided in error.details.localizedMessage and shown to the player verbatim (see Custom decline with localized message). |
For a complete list of error codes, see 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 iFrame 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 iFrame 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 iFrame 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 iFrame.- 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:
- Specific error codes (
decline.parent.notfound,decline.parent.failed) - Infinite retries until 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
- [ ] 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
- 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