Get Tournaments
Get Tournaments
Retrieve a list of tournaments with comprehensive filtering options.
Endpoint
Request Parameters
| Parameter |
Type |
Description |
| Operator |
Drop-down |
Filter by operator |
| Brand |
Drop-down |
Filter by brand |
| Provider |
Drop-down |
Filter by provider |
| Game ID’s / Game Name |
Text input |
Filter by specific game identifiers or names |
| Tournament Status |
Drop-down |
Filter by status: 0=Pending, 1=Active, 2=Complete, 3=Inactive |
| Tournament Name |
Text input |
Filter by tournament name |
This endpoint supports pagination for efficient data retrieval.
| Parameter |
Type |
Description |
| page |
Integer |
Page number (starting from 1) |
| page_size |
Integer |
Number of items per page |
Example Request
curl --location 'https://stgsinatragateway.groovegaming.com/tournaments/1/?page=1&page_size=10&status=0&operators=6&brands=55' \
--header 'Authorization: Bearer bm9mYXJAZ21haWwuY29tOmRkZA=='
Request URL Parameters
page=1 - First page
page_size=10 - 10 items per page
status=0 - Tournament status filter (0=Pending)
operators=6 - Operator ID filter
brands=55 - Brand ID filter
Response
Example Response
{
"total": 3,
"data": [
{
"auto_win": true,
"created_at": "2025-09-15T07:58:44Z",
"created_by": 27687,
"end_date": "2025-11-11 09:23:00",
"multiplier": 1.5,
"operator_brand_config": {
"6": {
"brands": [
"55"
]
}
},
"operator_template_id": "EXT-123456",
"participation_scope": 0,
"prize_type": 1,
"prizes": {
"SpecificPositionPrize": [
{
"position": 1,
"prize": 200
},
{
"position": 2,
"prize": 100
},
{
"position": 3,
"prize": 50
}
]
},
"providers": {
"26": {
"games": [
"82685"
]
}
},
"result_url": "https://example.com/tournament/result",
"rules": {
"BetToPointsConversion": [
{
"points_to_add": 5,
"required_wager": 50
}
],
"MinimumBetAmount": [
{
"required_wager": 10
}
],
"MinimumBetsOrRoundsRequired": [
{
"required_bets": 10,
"required_rounds": 1
}
],
"WinToPointsConversion": [
{
"points_to_add": 10,
"required_win": 100
}
]
},
"start_date": "2025-11-09 08:23:00",
"status": 2,
"tournament_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"tournament_name": "tournament test version 11",
"updated_at": "2025-09-15T08:09:40Z",
"updated_by": 27687
},
{
"auto_win": true,
"created_at": "2025-09-14T19:13:04Z",
"created_by": 3,
"end_date": "2025-10-11 09:23:00",
"multiplier": 1.5,
"operator_brand_config": {
"6": {
"brands": [
"55"
]
}
},
"operator_template_id": "EXT-123456",
"participation_scope": 0,
"prize_type": 1,
"prizes": {
"SpecificPositionPrize": [
{
"position": 1,
"prize": 200
},
{
"position": 2,
"prize": 100
},
{
"position": 3,
"prize": 50
}
]
},
"providers": {
"26": {
"games": [
"82685"
]
}
},
"result_url": "https://example.com/tournament/result",
"rules": {
"BetToPointsConversion": [
{
"points_to_add": 5,
"required_wager": 50
}
],
"MinimumBetAmount": [
{
"required_wager": 10
}
],
"MinimumBetsOrRoundsRequired": [
{
"required_bets": 10,
"required_rounds": 1
}
],
"WinToPointsConversion": [
{
"points_to_add": 10,
"required_win": 100
}
]
},
"start_date": "2025-10-09 08:23:00",
"status": 2,
"tournament_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"tournament_name": "avi ac11",
"updated_at": "2025-09-15T08:13:07Z",
"updated_by": 27687
},
{
"auto_win": true,
"created_at": "2025-09-01T07:09:44Z",
"created_by": 27687,
"end_date": "2025-09-11 09:24:00",
"multiplier": 1.5,
"operator_brand_config": {
"6": {
"brands": [
"55"
]
}
},
"operator_template_id": "EXT-123456",
"participation_scope": 0,
"prize_type": 1,
"prizes": {
"SpecificPositionPrize": [
{
"position": 1,
"prize": 200
},
{
"position": 2,
"prize": 100
},
{
"position": 3,
"prize": 50
}
]
},
"providers": {
"26": {
"games": [
"82685"
]
}
},
"result_url": "https://example.com/tournament/result",
"rules": {
"BetToPointsConversion": [
{
"points_to_add": 5,
"required_wager": 50
}
],
"MinimumBetAmount": [
{
"required_wager": 10
}
],
"MinimumBetsOrRoundsRequired": [
{
"required_bets": 10,
"required_rounds": 1
}
],
"WinToPointsConversion": [
{
"points_to_add": 10,
"required_win": 100
}
]
},
"start_date": "2025-09-09 08:23:00",
"status": 2,
"tournament_id": "550e8400-e29b-41d4-a716-446655440000",
"tournament_name": "tournament test version 11",
"updated_at": "2025-09-01T07:13:09Z",
"updated_by": 27687
}
],
"message": ""
}
Response Fields
| Field |
Type |
Description |
| total |
Integer |
Total number of tournaments available (used for pagination) |
| data |
Array |
Array of tournament objects |
| message |
String |
Additional message or error information (empty on success) |
Tournament Object Fields
| Field |
Type |
Description |
| tournament_id |
String (UUID) |
Unique identifier for the tournament |
| tournament_name |
String |
Display name of the tournament |
| status |
Integer |
Tournament status: 0=Pending, 1=Active, 2=Complete, 3=Inactive |
| start_date |
String |
Tournament start date and time (YYYY-MM-DD HH:MM:SS) |
| end_date |
String |
Tournament end date and time (YYYY-MM-DD HH:MM:SS) |
| auto_win |
Boolean |
Whether auto-win is enabled for this tournament |
| multiplier |
Number |
Point multiplier applied to tournament scoring |
| participation_scope |
Integer |
Defines who can participate (0=All players, other values for restrictions) |
| prize_type |
Integer |
Type of prizes awarded (1=Specific position prizes) |
| operator_template_id |
String |
External template identifier for the operator |
| result_url |
String |
URL endpoint for tournament results |
| created_at |
String |
Tournament creation timestamp (ISO 8601 format) |
| updated_at |
String |
Last update timestamp (ISO 8601 format) |
| created_by |
Integer |
User ID who created the tournament |
| updated_by |
Integer |
User ID who last updated the tournament |
Complex Object Fields
operator_brand_config
Defines which operators and brands can participate in the tournament.
{
"6": { // Operator ID
"brands": [
"55" // Array of brand IDs under this operator
]
}
}
providers
Specifies which game providers and specific games are included.
{
"26": { // Provider ID
"games": [
"82685" // Array of game IDs from this provider
]
}
}
prizes
Defines the prize structure for tournament winners.
{
"SpecificPositionPrize": [
{
"position": 1, // Leaderboard position
"prize": 200 // Prize amount for this position
}
]
}
rules
Tournament rules that define how players earn points and participation requirements.
| Rule Type |
Description |
| BetToPointsConversion |
Points awarded based on wager amounts |
| WinToPointsConversion |
Points awarded based on win amounts |
| MinimumBetAmount |
Minimum wager required to participate |
| MinimumBetsOrRoundsRequired |
Minimum number of bets/rounds required |
BetToPointsConversion Example:
{
"points_to_add": 5, // Points awarded
"required_wager": 50 // Wager amount needed to earn these points
}
WinToPointsConversion Example:
{
"points_to_add": 10, // Points awarded
"required_win": 100 // Win amount needed to earn these points
}
MinimumBetAmount Example:
{
"required_wager": 10 // Minimum bet amount to participate
}
MinimumBetsOrRoundsRequired Example:
{
"required_bets": 10, // Minimum number of bets required
"required_rounds": 1 // Minimum number of rounds required
}
Authentication
Authorization Required
This endpoint requires Bearer token authentication using your Sinatra back office credentials. All requests must include a valid Authorization header. Unauthorized requests will be rejected.
Required Header:
Authorization: Bearer <base64_encoded_credentials>
Token Generation:
base64_encoded_credentials = base64encode(sinatrauser:sinatrapassword)
Example:
- Sinatra Username:
myuser@example.com
- Sinatra Password:
mypassword
- Credentials String:
myuser@example.com:mypassword
- Base64 Encoded:
bXl1c2VyQGV4YW1wbGUuY29tOm15cGFzc3dvcmQ=
- Final Header:
Authorization: Bearer bXl1c2VyQGV4YW1wbGUuY29tOm15cGFzc3dvcmQ=
Replace with your actual Sinatra back office credentials.
Get Leaderboard
Get Leaderboard
Retrieve tournament leaderboard with player rankings sorted by points in descending order.
Endpoint
GET /tournaments/:version/leaderboard
Path Parameters
| Parameter |
Type |
Description |
| version |
Integer |
API version number |
Query Parameters
| Parameter |
Type |
Required |
Description |
| player_id |
String |
No |
Filter by specific player ID |
| brand_id |
Integer |
No |
Filter by specific brand ID |
| tournament_id |
String |
No |
Filter by specific tournament ID |
| tournament_status |
String |
No |
Filter by tournament status: 0=Pending, 1=Active, 2=Complete, 3=Inactive |
| tournament_end_date_from |
String |
No |
Filter tournaments ending from this date (format: YYYY-MM-DD) |
| tournament_end_date_to |
String |
No |
Filter tournaments ending until this date (format: YYYY-MM-DD) |
| format |
String |
No |
Response format (e.g., csv) - TBD |
| page |
Integer |
No |
Page number for pagination |
| page_size |
Integer |
No |
Number of items per page |
| require_totals |
Boolean |
No |
Whether to include total counts in response |
Default Date Range
Note: When searching by brand + player without specifying date filters, only leaderboards from the last 3 months are returned by default.
Filter Application Rules
Note: Status and date filters (tournament_status, tournament_end_date_from, tournament_end_date_to) are only applied when searching by brand + player. If only tournament_id is provided, these filters are ignored and only pagination parameters (page, page_size) are respected.
Tournament ID Validation
Important: If a tournament_id is provided along with brand + player filters, and the tournament does not match the other filter criteria (status, date range, etc.), the API will return an error indicating that the tournament does not exist.
Player + Brand + Tournament + format
Sorting
Results are automatically sorted by points in descending order (highest points first).
This endpoint supports pagination for efficient data retrieval.
Example Request
curl --location 'https://stgsinatragateway.groovegaming.com/tournaments/1/leaderboard?brand_id=3435&player_id=217850&tournament_status=1&tournament_end_date_from=2025-01-01&tournament_end_date_to=2025-12-31&page=1&page_size=10' \
--header 'Authorization: Bearer bm9mYXJAZ21haWwuY29tOmRkZA=='
Request URL Parameters
1 - API version in the URL path
brand_id=3435 - Filter by brand ID (optional)
player_id=217850 - Filter by player ID (optional)
tournament_status=1 - Filter for Active tournaments (optional)
tournament_end_date_from=2025-01-01 - Filter tournaments ending from this date (optional)
tournament_end_date_to=2025-12-31 - Filter tournaments ending until this date (optional)
page=1 - First page (optional)
page_size=10 - 10 items per page (optional)
Response
Example Response
{
"total": 3,
"data": [
{
"tournament_id": "2eab1af5-81e7-4d85-ad2e-fd4a23fae68a",
"brand_id": 3435,
"player_id": "217850",
"start_multiplier": 1,
"multiplier": 1,
"eligible": true,
"points": 250,
"position": 1,
"total_bets": 1001,
"total_wins": 500.5,
"total_rounds": 1,
"created_at": "2025-09-22T14:08:00Z"
},
{
"tournament_id": "2eab1af5-81e7-4d85-ad2e-fd4a23fae68a",
"brand_id": 3435,
"player_id": "217849",
"start_multiplier": 1,
"multiplier": 1,
"eligible": true,
"points": 140,
"position": 2,
"total_bets": 601.5,
"total_wins": 200.5,
"total_rounds": 1,
"created_at": "2025-09-22T14:08:00Z"
},
{
"tournament_id": "2eab1af5-81e7-4d85-ad2e-fd4a23fae68a",
"brand_id": 3435,
"player_id": "217848",
"start_multiplier": 1,
"multiplier": 1,
"eligible": true,
"points": 22,
"position": 3,
"total_bets": 102.5,
"total_wins": 20.5,
"total_rounds": 1,
"created_at": "2025-09-22T14:08:00Z"
}
],
"message": ""
}
Response Fields
| Field |
Type |
Description |
| total |
Integer |
Total number of players in the leaderboard (used for pagination) |
| data |
Array |
Array of leaderboard entry objects sorted by points (highest first) |
| message |
String |
Additional message or error information (empty on success) |
Leaderboard Entry Fields
| Field |
Type |
Description |
| tournament_id |
String (UUID) |
Unique identifier of the tournament this entry belongs to |
| brand_id |
Integer |
Brand identifier where the player is registered |
| player_id |
String |
Unique identifier of the player in the leaderboard |
| start_multiplier |
Number |
Initial multiplier value when player joined the tournament |
| multiplier |
Number |
Current multiplier applied to the player’s points |
| eligible |
Boolean |
Whether the player is eligible for prizes (true=eligible, false=ineligible) |
| points |
Integer |
Total points earned by the player in the tournament |
| position |
Integer |
Player’s current ranking position in the leaderboard (1=first place) |
| total_bets |
Number |
Total amount of bets placed by the player in the tournament |
| total_wins |
Number |
Total amount of wins earned by the player in the tournament |
| total_rounds |
Integer |
Total number of game rounds played by the player in the tournament |
| created_at |
String |
Timestamp when the player joined the tournament (ISO 8601 format) |
Key Features
- Automatic Sorting: Results are automatically sorted by points in descending order (highest points first)
- Player + Brand + Tournament Format: Each entry represents a unique combination of player, brand, and tournament
- Eligibility Tracking: The
eligible field indicates whether players qualify for prizes
- Multiplier Support: Both starting and current multipliers are tracked for each player
- Point Precision: Points are tracked as integers for accurate leaderboard positioning
Authentication
Authorization Required
This endpoint requires Bearer token authentication using your Sinatra back office credentials. All requests must include a valid Authorization header. Unauthorized requests will be rejected.
Required Header:
Authorization: Bearer <base64_encoded_credentials>
Token Generation:
base64_encoded_credentials = base64encode(sinatrauser:sinatrapassword)
Example:
- Sinatra Username:
myuser@example.com
- Sinatra Password:
mypassword
- Credentials String:
myuser@example.com:mypassword
- Base64 Encoded:
bXl1c2VyQGV4YW1wbGUuY29tOm15cGFzc3dvcmQ=
- Final Header:
Authorization: Bearer bXl1c2VyQGV4YW1wbGUuY29tOm15cGFzc3dvcmQ=
Replace with your actual Sinatra back office credentials.
Use Cases
- Display tournament rankings to players
- Show leaderboard on tournament pages
- Track player performance across tournaments
- Generate tournament reports
Tournament Prize
Tournament Prize Distribution
Info
The Tournament Prize API is used to distribute prizes to winners when a tournament concludes. This endpoint handles the prize allocation from Groove to the casino operator.
Overview
When a tournament ends and winners are determined, Groove sends Tournament Prize requests to the casino operator to distribute the prizes. The casino processes these requests by validating the tournament details, checking for duplicates, and updating the player’s balance accordingly.
Warning
**Important:** Tournament Prize requests implement idempotency controls. This means the same prize request should only be processed once. If a duplicate request is received, the system must return the original response with status code 200 and the "Success - Duplicate" status.
Flow Diagram
sequenceDiagram
participant Groove as Groove Platform
participant Casino as Casino Operator
participant Player as Player Account
Groove->>Casino: Tournament Prize Request
Casino->>Casino: Validate Signature
Casino->>Casino: Check Idempotency
Casino->>Casino: Validate Tournament ID
Casino->>Casino: Validate Player ID
Casino->>Casino: Process Prize
alt Prize Type: Amount
Casino->>Player: Credit Prize Amount
else Prize Type: Other
Casino->>Casino: Process Non-Monetary Prize
end
Casino->>Groove: Prize Response
Casino Responsibilities
The casino platform must perform the following operations when processing a Tournament Prize request:
-
Signature Validation
- Validate the
X-Groove-Signature header
- Recalculate the HMAC-SHA256 signature from the request body fields (instead of query parameters) and compare it to the header
- Reject the request if signature validation fails
-
Idempotency Check
- Check if a request with the same
transaction_id has been processed before
- If duplicate, return the original response with status “Success - Duplicate”
-
Tournament Validation
- Verify the tournament exists and is valid
- Confirm the player participated in the tournament
- Validate prize details match tournament configuration
-
Prize Processing
- For monetary prizes (prize_type = 1): Credit the prize amount to player’s balance
- For non-monetary prizes (prize_type = 2): Process according to operator’s implementation
- Store transaction details for auditing
Request Details
Endpoint
The casino URL can be either:
- The URL specified in the tournament definition
- If the tournament URL is empty, use the default wallet endpoint URL (same as regular game play)
{casino_URL}?request=tournamentWin&version=1.2
Request Method
POST
| Header |
Required |
Description |
| X-Groove-Signature |
Yes |
HMAC-SHA256 signature of the request body. See Signature Calculation |
| Content-Type |
Yes |
application/json |
| Original-Brand-ID |
No |
The operator’s original brand identifier, set by Groove |
Request Body Parameters
| Parameter |
Data Type |
Required |
Description |
| tournament_id |
String(UUID) |
Yes |
Unique tournament identifier Example: 123456-13456-123456-123456 |
| tournament_start_date |
DateTime |
Yes |
Tournament start date and time (UTC) Format: YYYY-MM-DD HH:MM:SS |
| tournament_end_date |
DateTime |
Yes |
Tournament end date and time (UTC) Format: YYYY-MM-DD HH:MM:SS |
| tournament_position |
Integer |
Yes |
Player’s final position in the tournament Example: 1 (for first place) |
| tournament_name |
String |
Yes |
Tournament name Example: Summer Slots Championship |
| template_id |
String |
No |
Operator template ID set on tournament configuration; may be empty Example: 1 |
| prize_type |
Integer |
Yes |
Prize type:
1 - Monetary amount
2 - Other (non-monetary) |
| player_id |
String |
Yes |
Player’s unique identifier Example: 12345 |
| casino_id |
Integer |
Yes |
Casino/Brand identifier Example: 12345 |
| points |
Integer |
Yes |
Player’s accumulated tournament points Example: 1500 |
| prize_value |
Decimal(32,10) |
Yes |
Prize amount; 0 for non-monetary prizes Example: 100.5 |
| prize_name |
String |
No |
Prize name (used mainly for non-monetary prizes); may be empty Example: Grand Prize |
| total_rounds |
Integer |
Yes |
Total number of rounds played in the tournament Example: 10 |
| total_bets |
Decimal(32,10) |
Yes |
Total amount wagered during the tournament Example: 100 |
| total_wins |
Decimal(32,10) |
Yes |
Total amount won during the tournament Example: 2 |
| timestamp |
DateTime |
Yes |
Request timestamp (UTC) Format: YYYY-MM-DD HH:MM:SS |
| transaction_id |
String(255) |
Yes |
Unique transaction identifier Format: {tournament_id}_{player_id}_{casino_id} Example: 123456-13456-123456-123456_12345_12345 |
Info
Groove always sends **all** of the fields above. Fields marked optional may contain an empty string (or `0` for `prize_value`). The `X-Groove-Signature` is computed over **every field present in the body** — see [Signature Calculation](#signature-calculation).
Example Request
POST {casino_URL}?request=tournamentWin&version=1.2
Content-Type: application/json
X-Groove-Signature: a268e60d3179de79196db9552d234c90715c9b9359b7c4ae3dca62afa1bf096c
Original-Brand-ID: 12345
{
"tournament_id": "123456-13456-123456-123456",
"tournament_start_date": "2025-08-27 00:00:00",
"tournament_end_date": "2025-08-27 00:00:00",
"tournament_position": 1,
"tournament_name": "Summer Slots Championship",
"template_id": "1",
"prize_type": 1,
"player_id": "12345",
"casino_id": 12345,
"points": 1500,
"prize_value": 100.5,
"prize_name": "Grand Prize",
"total_rounds": 10,
"total_bets": 100,
"total_wins": 2,
"timestamp": "2025-08-27 00:00:00",
"transaction_id": "123456-13456-123456-123456_12345_12345"
}
Response Details
Response Parameters
| Parameter |
Data Type |
Required |
Description |
| transaction_id |
String(UUID) |
Yes |
Unique transaction identifier Example: 11111-11111-11111-11111 |
| code |
Integer |
Yes |
Response code (see below) Example: 200 |
| status |
String |
Yes |
Response status message Example: Success - Duplicate |
| timestamp |
DateTime |
Yes |
Response timestamp Format: YYYY-MM-DD HH:MM:SS |
Example Success Response
{
"transaction_id": "11111-11111-11111-11111",
"code": 200,
"status": "Success",
"timestamp": "2025-08-27 00:00:00"
}
Example Duplicate Response
{
"transaction_id": "11111-11111-11111-11111",
"code": 200,
"status": "Success - Duplicate",
"timestamp": "2025-08-27 00:00:00"
}
Response Codes
Success Codes
| Code |
Status |
Description |
| 200 |
Success |
Prize successfully processed |
| 200 |
Success - Duplicate |
Prize already processed (idempotent response) |
Error Codes
| Code |
Status |
Description |
| 1 |
Internal Error |
Internal server error or general processing error |
| 110 |
Operation not allowed |
Operation not permitted for various reasons: • Invalid tournament • Player not eligible • Invalid prize details • Account restrictions |
Tip
Error codes follow the same standard as other transaction endpoints. For additional error codes that may apply, refer to [Appendix A: Transaction Response Status Codes](/appendix-a-transaction-response-status-codes/).
Implementation Notes
-
Idempotency Handling:
- Always store transaction IDs to identify duplicate requests
- Return the original response for duplicate transactions with the same transaction ID
- Maintain prize distribution records for auditing
-
Signature Validation:
- Tournament Prize uses the same HMAC-SHA256 algorithm as the standard Signature Validation, but the signed values are taken from the request body fields instead of the URL query parameters
- Sort the body field names alphabetically, concatenate their values only, and compute
HMAC-SHA256(concatenated_values, security_key)
- See Signature Calculation for the exact steps and code samples
-
Prize Processing:
- Monetary prizes should be credited to the player’s real balance
- Non-monetary prizes require custom implementation based on operator requirements
- Always validate prize amounts match tournament configuration
-
Error Handling:
- Return appropriate error codes based on validation results
- Log all prize distribution attempts for auditing purposes
- Include detailed error messages for troubleshooting
Security
Signature Calculation
Tournament Prize requests are signed with the same HMAC-SHA256 mechanism as the standard
Transaction API Signature Validation. The only difference
is that the signed values come from the request body fields instead of the URL query string.
To compute (and validate) the signature:
- Take all fields from the JSON request body. Exclude the
request field and treat nogsgameid as gameid for sorting — these are shared rules from the standard algorithm and do not apply to this endpoint’s body, but the logic is identical.
- Sort the field names alphabetically (ascending).
- Concatenate the field values only (not the names), in that sorted order, with no separators. Each value uses its textual form exactly as transmitted in the JSON — numbers without quotes and without trailing zeros (e.g.
100.50 → 100.5), strings without surrounding quotes.
- Compute
HMAC-SHA256(concatenated_values, security_key) using the casino’s security key.
- Hex-encode (lowercase) the result and send it in the
X-Groove-Signature header.
signature = HMAC_SHA256(concatenated_body_values, security_key)
Warning
**The signature covers every field present in the body.** Build the concatenated string by iterating over the fields you actually receive — do not hardcode a fixed subset, so that any future fields are handled automatically.
Signature Example
Using the example request above and security key test_key.
After sorting the field names alphabetically and concatenating the values:
| # |
Field |
Value |
| 1 |
casino_id |
12345 |
| 2 |
player_id |
12345 |
| 3 |
points |
1500 |
| 4 |
prize_name |
Grand Prize |
| 5 |
prize_type |
1 |
| 6 |
prize_value |
100.5 |
| 7 |
template_id |
1 |
| 8 |
timestamp |
2025-08-27 00:00:00 |
| 9 |
total_bets |
100 |
| 10 |
total_rounds |
10 |
| 11 |
total_wins |
2 |
| 12 |
tournament_end_date |
2025-08-27 00:00:00 |
| 13 |
tournament_id |
123456-13456-123456-123456 |
| 14 |
tournament_name |
Summer Slots Championship |
| 15 |
tournament_position |
1 |
| 16 |
tournament_start_date |
2025-08-27 00:00:00 |
| 17 |
transaction_id |
123456-13456-123456-123456_12345_12345 |
Concatenated string (the exact value passed to HMAC-SHA256):
12345123451500Grand Prize1100.512025-08-27 00:00:001001022025-08-27 00:00:00123456-13456-123456-123456Summer Slots Championship12025-08-27 00:00:00123456-13456-123456-123456_12345_12345
Result:
X-Groove-Signature: a268e60d3179de79196db9552d234c90715c9b9359b7c4ae3dca62afa1bf096c
Implementation Examples
Each example below produces the signature a268e60d3179de79196db9552d234c90715c9b9359b7c4ae3dca62afa1bf096c for the request body above with security key test_key.
Python
import hmac
import hashlib
def compute_tournament_signature(body: dict, security_key: str) -> str:
param_map = {}
for key, value in body.items():
if key == "request": # shared rule; not present in this body
continue
sort_key = "gameid" if key == "nogsgameid" else key
param_map[sort_key] = _stringify(value)
# Sort by field name, then concatenate the values only
concatenated = "".join(param_map[k] for k in sorted(param_map))
return hmac.new(
security_key.encode("utf-8"),
concatenated.encode("utf-8"),
hashlib.sha256,
).hexdigest()
def _stringify(value) -> str:
if isinstance(value, bool):
return "true" if value else "false"
if isinstance(value, float) and value.is_integer():
return str(int(value)) # 100.0 -> "100"
return str(value) # 100.5 -> "100.5", ints/strings as-is
# Example usage
security_key = "test_key"
body = {
"tournament_id": "123456-13456-123456-123456",
"tournament_start_date": "2025-08-27 00:00:00",
"tournament_end_date": "2025-08-27 00:00:00",
"tournament_position": 1,
"tournament_name": "Summer Slots Championship",
"template_id": "1",
"prize_type": 1,
"player_id": "12345",
"casino_id": 12345,
"points": 1500,
"prize_value": 100.5,
"prize_name": "Grand Prize",
"total_rounds": 10,
"total_bets": 100,
"total_wins": 2,
"timestamp": "2025-08-27 00:00:00",
"transaction_id": "123456-13456-123456-123456_12345_12345",
}
print("X-Groove-Signature:", compute_tournament_signature(body, security_key))
# X-Groove-Signature: a268e60d3179de79196db9552d234c90715c9b9359b7c4ae3dca62afa1bf096c
Golang
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"sort"
"strings"
)
func computeTournamentSignature(body []byte, securityKey string) (string, error) {
var fields map[string]interface{}
if err := json.Unmarshal(body, &fields); err != nil {
return "", err
}
paramMap := make(map[string]string)
for key, value := range fields {
if key == "request" { // shared rule; not present in this body
continue
}
sortKey := key
if key == "nogsgameid" {
sortKey = "gameid"
}
paramMap[sortKey] = fmt.Sprintf("%v", value)
}
// Sort by field name, then concatenate the values only
keys := make([]string, 0, len(paramMap))
for k := range paramMap {
keys = append(keys, k)
}
sort.Strings(keys)
var concatenated strings.Builder
for _, k := range keys {
concatenated.WriteString(paramMap[k])
}
h := hmac.New(sha256.New, []byte(securityKey))
h.Write([]byte(concatenated.String()))
return hex.EncodeToString(h.Sum(nil)), nil
}
func main() {
securityKey := "test_key"
body := []byte(`{"tournament_id":"123456-13456-123456-123456","tournament_start_date":"2025-08-27 00:00:00","tournament_end_date":"2025-08-27 00:00:00","tournament_position":1,"tournament_name":"Summer Slots Championship","template_id":"1","prize_type":1,"player_id":"12345","casino_id":12345,"points":1500,"prize_value":100.5,"prize_name":"Grand Prize","total_rounds":10,"total_bets":100,"total_wins":2,"timestamp":"2025-08-27 00:00:00","transaction_id":"123456-13456-123456-123456_12345_12345"}`)
signature, _ := computeTournamentSignature(body, securityKey)
fmt.Printf("X-Groove-Signature: %s\n", signature)
// X-Groove-Signature: a268e60d3179de79196db9552d234c90715c9b9359b7c4ae3dca62afa1bf096c
}
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;
public class GrooveTournamentSignature {
public static String computeSignature(Map<String, Object> body, String securityKey) throws Exception {
// TreeMap keeps the field names sorted alphabetically
TreeMap<String, String> params = new TreeMap<>();
for (Map.Entry<String, Object> entry : body.entrySet()) {
String key = entry.getKey();
if (key.equals("request")) { // shared rule; not present in this body
continue;
}
if (key.equals("nogsgameid")) {
key = "gameid";
}
params.put(key, stringify(entry.getValue()));
}
// Concatenate the values only
StringBuilder concatenated = new StringBuilder();
for (String value : params.values()) {
concatenated.append(value);
}
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(securityKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal(concatenated.toString().getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte b : hash) {
hex.append(String.format("%02x", b));
}
return hex.toString();
}
private static String stringify(Object value) {
if (value == null) {
return "";
}
if (value instanceof Boolean) {
return (Boolean) value ? "true" : "false";
}
if (value instanceof Double || value instanceof Float) {
// numbers without trailing zeros: 100.0 -> "100", 100.5 -> "100.5"
return BigDecimal.valueOf(((Number) value).doubleValue()).stripTrailingZeros().toPlainString();
}
return value.toString();
}
public static void main(String[] args) throws Exception {
String securityKey = "test_key";
Map<String, Object> body = new TreeMap<>();
body.put("tournament_id", "123456-13456-123456-123456");
body.put("tournament_start_date", "2025-08-27 00:00:00");
body.put("tournament_end_date", "2025-08-27 00:00:00");
body.put("tournament_position", 1);
body.put("tournament_name", "Summer Slots Championship");
body.put("template_id", "1");
body.put("prize_type", 1);
body.put("player_id", "12345");
body.put("casino_id", 12345);
body.put("points", 1500);
body.put("prize_value", 100.5);
body.put("prize_name", "Grand Prize");
body.put("total_rounds", 10);
body.put("total_bets", 100);
body.put("total_wins", 2);
body.put("timestamp", "2025-08-27 00:00:00");
body.put("transaction_id", "123456-13456-123456-123456_12345_12345");
System.out.println("X-Groove-Signature: " + computeSignature(body, securityKey));
// X-Groove-Signature: a268e60d3179de79196db9552d234c90715c9b9359b7c4ae3dca62afa1bf096c
}
}
TypeScript
import { createHmac } from "crypto";
function computeTournamentSignature(body: Record<string, unknown>, securityKey: string): string {
const paramMap: Record<string, string> = {};
for (const [key, value] of Object.entries(body)) {
if (key === "request") continue; // shared rule; not present in this body
const sortKey = key === "nogsgameid" ? "gameid" : key;
paramMap[sortKey] = stringify(value);
}
// Sort by field name, then concatenate the values only
const concatenated = Object.keys(paramMap)
.sort()
.map((k) => paramMap[k])
.join("");
return createHmac("sha256", securityKey).update(concatenated, "utf8").digest("hex");
}
function stringify(value: unknown): string {
if (typeof value === "boolean") return value ? "true" : "false";
return String(value); // numbers: no quotes, no trailing zeros; strings: as-is
}
// Example usage
const securityKey = "test_key";
const body = {
tournament_id: "123456-13456-123456-123456",
tournament_start_date: "2025-08-27 00:00:00",
tournament_end_date: "2025-08-27 00:00:00",
tournament_position: 1,
tournament_name: "Summer Slots Championship",
template_id: "1",
prize_type: 1,
player_id: "12345",
casino_id: 12345,
points: 1500,
prize_value: 100.5,
prize_name: "Grand Prize",
total_rounds: 10,
total_bets: 100,
total_wins: 2,
timestamp: "2025-08-27 00:00:00",
transaction_id: "123456-13456-123456-123456_12345_12345",
};
console.log("X-Groove-Signature:", computeTournamentSignature(body, securityKey));
// X-Groove-Signature: a268e60d3179de79196db9552d234c90715c9b9359b7c4ae3dca62afa1bf096c