Merchant ID, API key and approved source IP.
Your lobby.
Your wallet. One iframe.
Launch the exchange from your backend, embed it in your website, and keep control of every player balance through authenticated wallet callbacks.
https://api.betapidata.onlineConsume each launch URL within five minutes.
Shared server feed delivers odds through WebSocket.
Configure your merchant
Request merchant activation with your backend's public outbound IP, player website hostname, HTTPS lobby URL, wallet callback URL, and account currency. Test and production are configured per merchant.
- Store your merchant ID, API key and callback signing secret in a server-side secret store.
- Provide a public HTTPS callback with a valid certificate. Redirects and private-network callback targets are rejected.
- Approve both the actual backend IP and the player-site hostname. The
X-Origin-Domainheader does not replace IP verification. - Choose one currency for the merchant. Every launch and wallet event must use that currency.
- Complete test placement, duplicate delivery, refund and settlement checks before production activation.
Send your stable player reference in username. It is isolated under your merchant internally, so another merchant may use the same reference. Do not add our internal prefixes.
Create a launch session
/api/v1/launch.phpCall this endpoint from your backend after authenticating the player. Request and response bodies are JSON.
| Header | Value |
|---|---|
| Content-Type | application/json |
| X-Merchant-Id | Your assigned merchant code |
| X-Api-Key | Your private merchant API key |
| X-Origin-Domain | Your approved player hostname, e.g. merchant.example |
| JSON field | Requirement |
|---|---|
| username | Stable player reference; 1–100 letters, digits or _ . @ - |
| request_id | Fresh unique request reference; 8–100 letters, digits, _ or - |
| currency_code | Uppercase currency matching your merchant, such as BDT, USD or EUR |
<?php
$body = json_encode([
'username' => $authenticatedPlayerReference,
'request_id' => bin2hex(random_bytes(16)),
'currency_code' => 'BDT',
], JSON_THROW_ON_ERROR);
$ch = curl_init('https://api.betapidata.online/api/v1/launch.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 20,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Merchant-Id: ' . getenv('EXCHANGE_MERCHANT_ID'),
'X-Api-Key: ' . getenv('EXCHANGE_API_KEY'),
'X-Origin-Domain: merchant.example',
],
]);
$raw = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($raw === false || $http !== 200) {
throw new RuntimeException('Unable to launch the exchange.');
}
$result = json_decode($raw, true, 64, JSON_THROW_ON_ERROR);
$launchUrl = $result['launch_url'];Successful response
{
"success": true,
"error": "",
"launch_url": "https://api.betapidata.online/launch.php?token=ONE_TIME_TOKEN",
"expires_in": 300,
"currency_code": "BDT",
"merchant_mode": "test"
}The service checks your wallet balance through the configured callback before creating the launch. Lobby destination and branding come from merchant configuration, not an arbitrary browser-supplied URL.
Open the exchange
Return only the successful launch URL from your own authenticated backend to your frontend. Place it inside an iframe over your website content, with your own close control.
<iframe
title="Sports exchange"
src="LAUNCH_URL_FROM_YOUR_BACKEND"
allow="fullscreen; autoplay"
referrerpolicy="no-referrer"
style="width:100%;height:100dvh;border:0"
></iframe>Only approved merchant websites can embed the exchange. HTTPS and cookies are required. Some browsers restrict cross-site cookies; verify the embedded flow on your supported mobile and desktop browsers before enabling production.
The exchange logo returns to the exchange index within the iframe. An available configured logo is shown as an image; otherwise the configured brand name and icon are displayed. Merchant lobby navigation uses the approved lobby URL.
The launch token is single-use. A new launch for the same merchant and player replaces the previous session. The resulting player session lasts up to eight hours, subject to revocation or merchant suspension.
Implement the seamless wallet
We send HTTPS JSON requests to your configured wallet callback. Always authenticate the signature, verify the currency and locate the player using your own stable reference. Return HTTP 200 and the resulting available balance for a successful event.
Wallet changes follow the market portfolio’s worst-case exposure. A debit can differ from the new bet’s stake because earlier selections in the same market can offset its risk.
Callback headers
Content-Type: application/json
X-Merchant-Id: YOUR_MERCHANT_CODE
X-Timestamp: UNIX_SECONDS
X-Signature: LOWERCASE_HEX_HMAC_SHA256
X-Idempotency-Key: STABLE_EVENT_REFERENCE| Action | Wallet behavior |
|---|---|
| balance | Read the current available player balance. No debit or credit. |
| debit | Deduct the positive increase in the market portfolio’s worst-case liability once. Refuse insufficient funds. |
| debit_confirm | Record confirmation of an accepted order. Informational only; do not deduct again. |
| credit | Credit the supplied amount once when an accepted order reduces portfolio exposure, or when a confirmed rejection releases a reservation. |
| settlement | Credit the supplied positive final amount once: released market reserve plus the market’s net result. The amount already includes the result; do not add profit again. |
Balance request and response
{
"action": "balance",
"request_id": "launch_d07c1e9f38a44b25",
"username": "player123",
"currency": "BDT"
}{ "success": true, "balance": 1000.00, "currency": "BDT" }Order wallet event
{
"action": "debit",
"order_id": "12345678",
"username": "player123",
"amount": 10.00,
"currency": "BDT"
}Order events may include additional match, market, selection and settlement metadata. Use the supplied wallet amount rather than recomputing it from a displayed stake. Available balances must be finite, non-negative numbers with at most two decimal places. Never return secrets or internal database errors.
Confirm settlement metadata and adjustment semantics in your merchant acceptance test. A lost callback response is an uncertain outcome: retain the pending event and reconcile it before manually refunding or sending a different credit.
Verify the original request bytes
Compute a lowercase hexadecimal HMAC-SHA256 over X-Timestamp + "." + raw_request_body with your callback secret. Compare using a constant-time function and enforce a short timestamp window. Parse JSON only after authenticating it.
<?php
$raw = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$merchant = $_SERVER['HTTP_X_MERCHANT_ID'] ?? '';
$expectedMerchant = getenv('EXCHANGE_MERCHANT_ID');
$secret = getenv('EXCHANGE_CALLBACK_SECRET');
if (!is_string($raw) || strlen($raw) > 65536
|| !ctype_digit($timestamp) || abs(time() - (int)$timestamp) > 60
|| !hash_equals($expectedMerchant, $merchant)
|| !hash_equals(hash_hmac('sha256', $timestamp . '.' . $raw, $secret), $signature)) {
http_response_code(401);
exit;
}
$event = json_decode($raw, true, 64, JSON_THROW_ON_ERROR);
$eventKey = $_SERVER['HTTP_X_IDEMPOTENCY_KEY'] ?? '';This snippet authenticates transport only. Your handler must then validate fields and apply the wallet change and event record together in one database transaction.
Process each wallet event once
- Require a valid signed merchant event. A timestamp check alone does not prevent replay.
- Use a unique database constraint on merchant and
X-Idempotency-Key, with a stored hash of the authenticated payload. - Lock the player wallet, verify funds, apply the requested change and save the complete successful response in one transaction.
- For a matching duplicate event, return the saved response without changing the wallet again. Reject a reused key with a different payload.
- Keep failed or uncertain events for reconciliation. A network timeout does not prove a wallet event failed.
Do not blindly retry launch requests with the same request_id. Duplicate launch references are rejected. If a token response is lost, request a new launch with a fresh reference; the launch balance check itself does not debit the wallet.
Handle errors without exposing them to players
| HTTP | Typical cause | Action |
|---|---|---|
| 400 / 422 | Invalid fields or currency mismatch | Correct the server-side request. |
| 401 | Invalid merchant credentials or ended player session | Verify credentials, or create a new player launch after authentication. |
| 403 | Unapproved source IP or domain | Check the registered outbound IP and website. |
| 409 | Duplicate launch request reference | Use a new launch reference. |
| 410 | Used, expired or revoked launch token | Create a new authenticated launch. |
| 502 / 503 | Wallet callback or service unavailable | Show a short retry message and retain the request reference for support. |
Player-facing interfaces should show a safe message such as “Unable to load. Please try again later.” Keep technical diagnostics in restricted server logs. Never put API keys, callback secrets or provider addresses in client bundles, iframe query parameters or analytics.
Run the acceptance checklist
Test merchants use per-currency stake and liability limits. Production activation is an explicit merchant configuration change. A live data feed does not automatically make a test merchant a production merchant.
Merchant billing and self-service
This release covers the authenticated exchange iframe and seamless-wallet order flow. Self-service registration, merchant dashboard, subscription packages, manual-crypto or NOWPayments top-ups, and GGR-wallet billing are planned separately and are not exposed as public endpoints in this release.
The proposed billing model includes instant or 7 / 15 / 30-day settlement, configurable minimum stock, regular or advanced merchant risk policies, and signed pending GGR carried forward before billing. Player wins must offset pending GGR rather than create an automatic merchant-wallet credit. These policies require a dedicated billing ledger and acceptance tests before activation.