Oyuneks Bayi API v1
TR EN OpenAPI 3.1 Postman llms.txt Get a test key
Oyuneks Dealer API v1

Connect e-pin and top-up sales to your own system

Pull the catalog and your dealer prices, validate the player account, place the order and receive the code within seconds. Balance and ledger live in the same API. Price, stock and delivery changes reach you as signed webhooks.

The dealer panel and the API share the SAME core: the two channels never show a different price, stock or error. What you see in the panel is what the API returns.

https://oyuneks.com/bayi-api/v1
JSON Bearer key Idempotency-Key Signed webhooks Test key TRY UTF-8 · ISO-8601 (+03:00)

Quick start · First order in five minutes

  1. In the dealer panel (Integration > API keys) create a TEST key first. Orders placed with a test key spend no money and return TEST- prefixed dummy codes.
  2. Call GET /bayi-api/v1/me to confirm the key belongs to the right account and to see its scopes.
  3. Call GET /bayi-api/v1/catalog/products for product ids, prices and the required-field schema. Never invent ids or field names, always read them here.
  4. Place a test order with POST /bayi-api/v1/orders (do not forget the Idempotency-Key header). Retry with the same key: the same response comes back and no second order is created.
  5. When the flow works, create a LIVE key in the panel and swap the token. The code stays the same; the only difference is that money now moves.
  6. Stop polling: register your address with PUT /bayi-api/v1/webhooks, store the signing secret and verify the link with POST /bayi-api/v1/webhooks/test.
1) Verify the key - cURL
curl "https://oyuneks.com/bayi-api/v1/me" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
1) Verify the key - PHP
<?php
$ch = curl_init("https://oyuneks.com/bayi-api/v1/me");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"],
]);
$yanit = json_decode(curl_exec($ch), true);
$kod = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($kod >= 400) {
    // Dallanmayi HER ZAMAN error.code uzerinden yap; message degisebilir.
    throw new \RuntimeException($yanit["error"]["code"] . ": " . $yanit["error"]["message"]);
}
print_r($yanit["data"]);
1) Verify the key - Node.js
const yanit = await fetch("https://oyuneks.com/bayi-api/v1/me", {
  method: "GET",
  headers: {
    "Authorization": "Bearer oyxb_test_KENDI_ANAHTARIN"
  }
});

const govde = await yanit.json();
if (!yanit.ok) {
  // error.code sabittir; message insan icindir.
  throw new Error(govde.error.code + ": " + govde.error.message);
}
console.log(govde.data);
2) Place a test order - cURL
curl -X POST "https://oyuneks.com/bayi-api/v1/orders" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: SIP-2026-0001-1" \
  -d '{"externalRef":"SIP-2026-0001","waitForStock":true,"items":[{"productId":6,"quantity":2,"expectedPrice":"98.70"},{"productId":1421,"quantity":1,"fields":{"playerId":"5123456789"}}]}'
2) Place a test order - PHP
<?php
$ch = curl_init("https://oyuneks.com/bayi-api/v1/orders");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => ["Authorization: Bearer oyxb_test_KENDI_ANAHTARIN", "Content-Type: application/json", "Idempotency-Key: SIP-2026-0001-1"],
    CURLOPT_POSTFIELDS => json_encode([
    "externalRef" => "SIP-2026-0001",
    "waitForStock" => true,
    "items" => [
        [
            "productId" => 6,
            "quantity" => 2,
            "expectedPrice" => "98.70"
        ],
        [
            "productId" => 1421,
            "quantity" => 1,
            "fields" => [
                "playerId" => "5123456789"
            ]
        ]
    ]
]),
]);
$yanit = json_decode(curl_exec($ch), true);
$kod = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($kod >= 400) {
    // Dallanmayi HER ZAMAN error.code uzerinden yap; message degisebilir.
    throw new \RuntimeException($yanit["error"]["code"] . ": " . $yanit["error"]["message"]);
}
print_r($yanit["data"]);
2) Place a test order - Node.js
const yanit = await fetch("https://oyuneks.com/bayi-api/v1/orders", {
  method: "POST",
  headers: {
    "Authorization": "Bearer oyxb_test_KENDI_ANAHTARIN",
    "Content-Type": "application/json",
    "Idempotency-Key": "SIP-2026-0001-1"
  },
  body: JSON.stringify({"externalRef":"SIP-2026-0001","waitForStock":true,"items":[{"productId":6,"quantity":2,"expectedPrice":"98.70"},{"productId":1421,"quantity":1,"fields":{"playerId":"5123456789"}}]})
});

const govde = await yanit.json();
if (!yanit.ok) {
  // error.code sabittir; message insan icindir.
  throw new Error(govde.error.code + ": " + govde.error.message);
}
console.log(govde.data);
The Idempotency-Key header is required on POST /orders. Retrying with the same key returns the first result and never creates a second order.

Authentication

Send Authorization: Bearer <key> on every request. Keys are created in the dealer panel and shown ONCE at creation time; only a SHA-256 digest is stored. Scopes, IP restrictions and rate limits are chosen per key.

If a key leaks, revoke it in the panel: it dies instantly, requests with the old key get 401 YETKISIZ and the attempt is written to the dealer audit log. Scopes CANNOT be added later; create a new key instead.

The key prefix states the mode: oyxb_live_ is live, oyxb_test_ is test. The mode is baked into the token and cannot be overridden by the request body.

Scope What it allows
catalog:read Read catalog and prices
orders:read Read orders and delivered codes
orders:write Create and cancel orders (spends balance)
balance:read Read balance and ledger
webhooks:manage Manage the webhook subscription

Test environment

There is NO separate host: the same base URL runs in test mode with an oyxb_test_ key. Catalog and prices are real and the flow is real, but Basket / Orders / Stocks / Customers are never touched, no balance is spent, codes come back prefixed with TEST- and no top-up is credited.

Test orders have ids like "test_12" and are read from GET /orders/{orderId}. Webhooks are REALLY sent in test mode as well; the payload says "mode":"test" so you can branch on it.

A GB (type=goldbar) test order MIMICS THE REAL FLOW: the response is processing with no code and no receipt, and about a minute later GET /orders/{orderId} shows it delivered (receipt TEST-GB-...). The order.delivered webhook arrives at that moment too. In production an operator delivers it in-game, so exercise the waiting state with a test key before going live.

Going live is a one-line change: swap the token.

Conventions

Request headers

Header Meaning
Authorization Bearer oyxb_live_... ya da oyxb_test_... (ZORUNLU)
Idempotency-Key POST /orders ucunda ZORUNLU; en fazla 64 karakter. Aynı anahtar + aynı gövde = aynı sipariş (iş tekrar çalışmaz), aynı anahtar + farklı gövde = 409. Anahtar 24 saat hatırlanır; bu süreden SONRA aynı anahtarla gelen istek YENİ sipariş oluşturur.
If-None-Match Katalog uçlarında: gövde değişmediyse 304 döner, kota harcanmaz.
Content-Type application/json (form-data da kabul edilir).

Response headers

Header Meaning
X-Request-Id İsteğin kimliği (26 karakter). Destek kaydında bu numarayı verin, istek tek sorguyla bulunur.
X-RateLimit-Limit Bu dakika için geçerli tavan (okuma ve yazma AYRI pencere).
X-RateLimit-Remaining Bu dakika içinde kalan istek hakkı.
X-RateLimit-Reset Pencerenin sıfırlanacağı zaman (unix saniye).
Retry-After Yalnız 429 yanıtında: kaç saniye beklenmeli.
ETag Katalog uçlarında: sonraki istekte If-None-Match ile geri gönderin.
Idempotent-Replayed true ise bu yanıt ÖNCEKİ isteğin saklanan sonucudur; yeni sipariş OLUŞMADI.

Errors

The same envelope everywhere. Branch on code: message is for humans and may change, code is stable. With retryable=true the same request may be retried later; with false, retrying without fixing the request returns the same result.

The details field carries the machine readable specifics (required/available amount, current price, offending fields). It is {} when empty.

Error envelope
{
    "error": {
        "code": "INSUFFICIENT_BALANCE",
        "message": "Bakiye yetersiz.",
        "retryable": false,
        "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE",
        "details": {
            "required": "608.90",
            "available": "120.00"
        }
    }
}
HTTP code Meaning and what to do Retry
503 API_KAPALI Dealer API is temporarily switched off (maintenance). Retry after a while; no client change needed. yes
401 YETKISIZ Key missing, invalid, revoked, IP not allowed, or the account is not a dealer. Check the key and its IP list in the panel. If the key leaked, revoke it and create a new one. no
403 BAYI_ASKIDA The dealership is suspended. Contact support; retrying will not help. no
403 API_ERISIMI_KAPALI Dealership is active but API access is disabled for the account. Ask your dealer representative; access is enabled by our team. no
403 KAPSAM_YOK The key is valid but lacks the scope this endpoint requires. Create a new key that includes the scope (scopes cannot be added later). no
429 RATE_LIMITED Rate limit exceeded. Wait the number of seconds in Retry-After. Read and write windows are separate. yes
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
404 NOT_FOUND Record or endpoint not found. Verify the id and the path. Another dealer's order also returns "not found" (we do not leak existence). no
405 METHOD_NOT_ALLOWED The endpoint does not accept this HTTP method. The correct method is listed in the endpoint reference on this page. no
422 INSUFFICIENT_BALANCE Insufficient balance. details.required and details.available are in the response. Top up and retry. no
422 PRICE_CHANGED The expectedPrice you sent differs from the current price; the order was NOT created. details.current holds the current price. Refresh the catalog and retry with it. no
422 OUT_OF_STOCK Not enough stock (waitForStock=false was sent). Retry later, or send waitForStock=true to let the order wait in queue. yes
422 INVALID_FIELDS Required fields are missing or violate the length constraint. details.fields lists the offending fields. Read the schema from GET /catalog/products/{productId}. no
422 INVALID_PLAYER_ID The player information could not be validated by the supplier. Check with POST /orders/validate before ordering; ask the customer for the correct id. no
422 LIMIT_EXCEEDED Daily purchase limit or per-item quantity cap exceeded. See the limits object in GET /me; the daily limit resets at midnight. no
422 PRODUCT_DISABLED The product is not on sale or has no price defined. Refresh the catalog; you will get a product.enabled event when it is back. no
422 PRODUCT_NOT_ALLOWED The product cannot be sold through the dealer channel. Such products are not listed by the catalog endpoints either; drop them from the body. no
422 NOT_CANCELLABLE A goldbar item cannot be cancelled through this endpoint; it is delivered by hand. Contact support to cancel. E-pin/top-up items in the same order are cancelled as usual. no
422 MIN_QTY Below the product minimum order quantity. Use limits.minQty from the catalog. no
409 IDEMPOTENCY_MISMATCH The same Idempotency-Key was previously used with a DIFFERENT body. Generate a new key per new order (your order number plus an attempt counter works well). no
409 ORDER_IN_PROGRESS An order with the same key is being processed right now. Wait a few seconds and read the result with GET /orders/by-ref/{externalRef}. yes
501 NOT_IMPLEMENTED The endpoint is announced but not working yet. No endpoint currently returns this code; it is listed for historical completeness. no
500 INTERNAL Unexpected server error. Retry; if it persists open a support ticket with the X-Request-Id. yes

Rate limits

Per key, per minute: 300 reads and 60 writes (orders included). READ AND WRITE ARE SEPARATE WINDOWS: bulk catalog pulls do not eat your ordering budget. HEAD counts as a read.

Every response returns X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. On overflow you get 429 plus Retry-After seconds; wait and continue.

Do not pull the whole catalog every minute: take 304s with ETag/If-None-Match and read price moves from the price.changed event or the GET /catalog/prices?since= delta.

Account & balance

Which account the key belongs to, limits and money.

GET /bayi-api/v1/me scope: (herhangi)

Key health check #

Dealer id, company name, tier, available balance, daily limit and the scopes of the key. The mode field returns "live" or "test"; this should be the first call of any integration. Requires NO scope: every valid key can verify itself.

Example request
curl "https://oyuneks.com/bayi-api/v1/me" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "dealerId": 385841,
        "company": "ÖRNEK BİLİŞİM LTD. ŞTİ.",
        "tier": "Bronz",
        "tierId": 1,
        "balance": "48250.00",
        "currency": "TRY",
        "limits": {
            "dailyPurchase": "250000.00",
            "dailyUsed": "1200.00",
            "maxQtyPerOrder": 500
        },
        "apiEnabled": true,
        "key": {
            "name": "Canlı anahtar",
            "masked": "oyxb_live_a91f...3c0e",
            "scopes": [
                "catalog:read",
                "orders:read",
                "orders:write",
                "balance:read"
            ],
            "rateReadPerMin": 300,
            "rateWritePerMin": 60
        },
        "apiVersion": "v1",
        "serverTime": "2026-08-28T14:05:00+03:00"
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}
GET /bayi-api/v1/balance scope: balance:read

Available balance #

Available balance, blocked amount, pending bank transfers and the breakdown (balance + refundable + bonus). The available value is the very same number the order gate reads.

Example request
curl "https://oyuneks.com/bayi-api/v1/balance" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "available": "48250.00",
        "blocked": "0.00",
        "breakdown": {
            "balance": "46250.00",
            "refundable": "1500.00",
            "bonus": "500.00"
        },
        "pendingDeposits": "5000.00",
        "currency": "TRY",
        "asOf": "2026-08-28T14:05:00+03:00"
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}
GET /bayi-api/v1/balance/transactions scope: balance:read

Ledger entries (cursor paged) #

The ledger. Order rows return a NEGATIVE amount and carry orderId + externalRef; use balanceAfter for reconciliation. Cursor paging: send the nextCursor value back as ?cursor=.

Name In Type Required Description
from query string no Start date (inclusive). An unparsable date is ignored.
to query string no End date (inclusive).
type query string
order | refund | deposit | adjustment | other
no Entry type filter. Unknown types return "other" and typeId carries the raw value.
cursor query string no The nextCursor from the previous response. Digits only.
limit query integer
default: 50
no Page size (1-200).
Example request
curl "https://oyuneks.com/bayi-api/v1/balance/transactions?from=2026-08-01&to=2026-08-28" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "items": [
            {
                "id": 3012402,
                "type": "order",
                "typeId": 1,
                "amount": "-197.40",
                "balanceAfter": "48052.60",
                "orderId": 184213,
                "externalRef": "SIP-2026-0001",
                "description": "Sipariş ödemesi",
                "createdAt": "2026-08-28T14:05:00+03:00"
            },
            {
                "id": 3012301,
                "type": "deposit",
                "typeId": 2,
                "amount": "25000.00",
                "balanceAfter": "48250.00",
                "orderId": null,
                "externalRef": null,
                "description": "Havale",
                "createdAt": "2026-08-28T09:12:00+03:00"
            }
        ],
        "nextCursor": "3012301",
        "currency": "TRY"
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no

Catalog & price

Product ids, your dealer price, stock and the required-field schema. Never invent ids or field names, always read them here.

GET /bayi-api/v1/catalog/games scope: catalog:read

Game list #

Games (top categories) that have products available to dealers, their sub-categories (categories) and product counts. Pass categories[].category to the category filter of the product list. Pass the slug from here to the game filter of the product list, not the category id: ids may change when categories are merged, slugs cannot.

Returns an ETag. Send it back as If-None-Match and you get 304 with no body when nothing changed.

Name In Type Required Description
If-None-Match header string no The ETag of the previous response. Returns 304 when the body did not change.
Example request
curl "https://oyuneks.com/bayi-api/v1/catalog/games" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "items": [
            {
                "game": "pubg-mobile",
                "name": "PUBG Mobile",
                "categoryId": 25,
                "productCount": 34,
                "categories": [
                    {
                        "category": "pubg-mobile-uc",
                        "name": "PUBG Mobile UC",
                        "categoryId": 251,
                        "productCount": 34
                    }
                ]
            },
            {
                "game": "knight-online",
                "name": "Knight Online",
                "categoryId": 12,
                "productCount": 18,
                "categories": []
            }
        ]
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}
GET /bayi-api/v1/catalog/products scope: catalog:read

Product list (cursor paged) #

Dealer price, list price, stock state, quantity bounds, required-field schema and delivery estimate. price is YOUR price (tier plus any dealer-specific override), listPrice is the retail price on the site. An ETag is returned: send it back with If-None-Match and you get a 304 when nothing changed.

Returns an ETag. Send it back as If-None-Match and you get 304 with no body when nothing changed.

Name In Type Required Description
type query string
epin | topup | goldbar
no epin = a code is delivered, topup = the player account is credited (no code, a receipt instead), goldbar = game currency (GB) delivered in-game by hand to the character name, no code. Any other value returns 400.
game query string no Game slug (from GET /catalog/games).
category query string no Sub-category slug (categories[].category in GET /catalog/games). Works alone or together with game.
q query string no Search in the product, game or category name.
inStock query boolean no Send 1 to return only in-stock products ("0", "false", "no", "off" and empty count as off).
updatedSince query string no NOT APPLIED YET: products carry no update stamp, so the full list is returned and the note field says so. Use ETag or GET /catalog/prices?since= for incremental sync.
cursor query string no The nextCursor from the previous response (last product id).
limit query integer
default: 50
no Page size (1-200).
If-None-Match header string no The ETag of the previous response.
Example request
curl "https://oyuneks.com/bayi-api/v1/catalog/products?type=epin&game=pubg-mobile" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "items": [
            {
                "productId": 6,
                "name": "Knight Online 1020 Cash",
                "type": "epin",
                "game": "knight-online",
                "category": "knight-online-cash",
                "region": null,
                "listPrice": "105.00",
                "price": "98.70",
                "currency": "TRY",
                "vatIncluded": true,
                "stock": {
                    "mode": "count",
                    "count": 420
                },
                "limits": {
                    "minQty": 1,
                    "maxQty": 500
                },
                "requiredFields": [],
                "delivery": {
                    "mode": "instant",
                    "etaSeconds": 5
                },
                "enabled": true,
                "updatedAt": null
            },
            {
                "productId": 1421,
                "name": "PUBG Mobile 660 UC",
                "type": "topup",
                "game": "pubg-mobile",
                "category": "pubg-mobile-uc",
                "region": null,
                "listPrice": "428.90",
                "price": "411.50",
                "currency": "TRY",
                "vatIncluded": true,
                "stock": {
                    "mode": "instant"
                },
                "limits": {
                    "minQty": 1,
                    "maxQty": 500
                },
                "requiredFields": [
                    {
                        "key": "playerId",
                        "label": "Oyuncu ID",
                        "type": "string",
                        "minLength": 8,
                        "maxLength": 12,
                        "required": true,
                        "pattern": "^.{8,12}$",
                        "placeholder": null
                    }
                ],
                "delivery": {
                    "mode": "instant",
                    "etaSeconds": 30
                },
                "enabled": true,
                "updatedAt": null
            }
        ],
        "nextCursor": "1421",
        "count": 2
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
GET /bayi-api/v1/catalog/products/{productId} scope: catalog:read

Single product #

The full view of one product. The required-field SCHEMA is published here: field keys (playerId, server, zoneId, character...) are derived from the product labels. Do not invent them, read them here; the KEY NAME matters, not the order.

Returns an ETag. Send it back as If-None-Match and you get 304 with no body when nothing changed.

Name In Type Required Description
productId path integer yes Product id (digits only).
If-None-Match header string no The ETag of the previous response.
Example request
curl "https://oyuneks.com/bayi-api/v1/catalog/products/1421" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "productId": 1421,
        "name": "PUBG Mobile 660 UC",
        "type": "topup",
        "game": "pubg-mobile",
        "category": "pubg-mobile-uc",
        "region": null,
        "listPrice": "428.90",
        "price": "411.50",
        "currency": "TRY",
        "vatIncluded": true,
        "stock": {
            "mode": "instant"
        },
        "limits": {
            "minQty": 1,
            "maxQty": 500
        },
        "requiredFields": [
            {
                "key": "playerId",
                "label": "Oyuncu ID",
                "type": "string",
                "minLength": 8,
                "maxLength": 12,
                "required": true,
                "pattern": "^.{8,12}$",
                "placeholder": null
            }
        ],
        "delivery": {
            "mode": "instant",
            "etaSeconds": 30
        },
        "enabled": true,
        "updatedAt": null
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
404 NOT_FOUND Record or endpoint not found. Verify the id and the path. Another dealer's order also returns "not found" (we do not leak existence). no
GET /bayi-api/v1/catalog/prices scope: catalog:read

Price/stock dump and delta #

The light version of the product list: only productId, price, stock and enabled. Built for price-sync bots. With ?since you get a REAL DELTA (products whose price/stock/state changed after that moment, each row carrying a real updatedAt). If the snapshot for the account does not exist yet the filter is NOT applied: the full list plus a warning note is returned, because an empty list would make the bot believe nothing changed.

Returns an ETag. Send it back as If-None-Match and you get 304 with no body when nothing changed.

Name In Type Required Description
since query string no Products changed after this moment. An unparsable value returns the full list plus a note.
cursor query string no The nextCursor from the previous response.
limit query integer
default: 200
no Page size (1-200).
If-None-Match header string no The ETag of the previous response.
Example request
curl "https://oyuneks.com/bayi-api/v1/catalog/prices?since=2026-08-28T09%3A00%3A00%2B03%3A00&limit=200" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "items": [
            {
                "productId": 6,
                "listPrice": "105.00",
                "price": "96.20",
                "currency": "TRY",
                "stock": {
                    "mode": "count",
                    "count": 418
                },
                "enabled": true,
                "updatedAt": "2026-08-28T14:30:00+03:00"
            }
        ],
        "nextCursor": null,
        "count": 1,
        "asOf": "2026-08-28T14:35:00+03:00"
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Account validation

For top-up products, check the player information BEFORE crediting.

POST /bayi-api/v1/orders/validate scope: orders:write

Pre-validate player information #

Asks the supplier about the player information BEFORE ordering; no money moves. supported=false means the product has no pre-validation (it does not block ordering). valid=null means "could not check right now": a technical failure never blocks a sale, the same check runs again in the core at order time. The response also returns the requiredFields schema.

Request body

Field Type Required Description
productId integer yes Product id.
fields object no Keys and values from the product requiredFields schema.
Example request
curl -X POST "https://oyuneks.com/bayi-api/v1/orders/validate" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN" \
  -H "Content-Type: application/json" \
  -d '{"productId":1421,"fields":{"playerId":"5123456789","server":"7012"}}'
Example response - HTTP 200
{
    "data": {
        "supported": true,
        "valid": true,
        "nickname": "OyuncuAdi",
        "validUntil": "2026-08-28T14:10:00+03:00",
        "requiredFields": [
            {
                "key": "playerId",
                "label": "Oyuncu ID",
                "type": "string",
                "minLength": 8,
                "maxLength": 12,
                "validate": true
            }
        ],
        "message": null
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
404 NOT_FOUND Record or endpoint not found. Verify the id and the path. Another dealer's order also returns "not found" (we do not leak existence). no

Orders

Create, read and cancel pending items. The panel and the API share the SAME core, so the two channels never show a different price, stock or error.

POST /bayi-api/v1/orders scope: orders:write

Create an order #

Creates an order and debits your balance. The Idempotency-Key header is REQUIRED. Up to 50 items per request. If you send expectedPrice and the price moved, the order is NOT created (PRICE_CHANGED). With waitForStock=false the order never starts when stock is short. The response is 201 (final) or 202 (delivery in progress). Repeating with the same key returns the same body plus the Idempotent-Replayed: true header and creates NO new order, so retrying after a network error is safe.

Name In Type Required Description
Idempotency-Key header string yes Your unique reference, max 64 characters. Generate a new one for every NEW order. A key is remembered for 24 hours.

Request body

Field Type Required Description
externalRef string no Your own order number. It comes back in reports, the ledger and webhook payloads.
waitForStock boolean
default: true
no true: queue the order and deliver when stock arrives. false: do not start at all when stock is short (OUT_OF_STOCK).
items array yes Items (1 to 50). Each item: productId (required), quantity (required), fields (per schema for top-up), expectedPrice (optional price lock).
Example request
curl -X POST "https://oyuneks.com/bayi-api/v1/orders" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: SIP-2026-0001-1" \
  -d '{"externalRef":"SIP-2026-0001","waitForStock":true,"items":[{"productId":6,"quantity":2,"expectedPrice":"98.70"},{"productId":1421,"quantity":1,"fields":{"playerId":"5123456789"}}]}'
Example response - HTTP 201
{
    "data": {
        "orderId": 184213,
        "externalRef": "SIP-2026-0001",
        "channel": "dealer-api",
        "status": "delivered",
        "mode": "live",
        "total": "608.90",
        "balanceAfter": "47641.10",
        "createdAt": "2026-08-28T14:05:00+03:00",
        "items": [
            {
                "itemId": "184213-1",
                "productId": 6,
                "name": "Knight Online 1020 Cash",
                "quantity": 2,
                "unitPrice": "98.70",
                "status": "delivered",
                "codes": [
                    {
                        "code": "KO10-2XQ4-9WPA",
                        "serial": null,
                        "expiresAt": null
                    },
                    {
                        "code": "KO10-7MTB-3VZC",
                        "serial": null,
                        "expiresAt": null
                    }
                ],
                "receipt": null,
                "failCode": null,
                "failReason": null
            },
            {
                "itemId": "184213-2",
                "productId": 1421,
                "name": "PUBG Mobile 660 UC",
                "quantity": 1,
                "unitPrice": "411.50",
                "status": "delivered",
                "codes": [],
                "receipt": "TOPUP-9F31A2",
                "failCode": null,
                "failReason": null
            }
        ]
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

HTTP 202 - At least one item is waiting for stock or being pulled from the supplier (status=processing). Learn the result from the webhook or GET /orders/{orderId}.

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
422 INSUFFICIENT_BALANCE Insufficient balance. details.required and details.available are in the response. Top up and retry. no
422 PRICE_CHANGED The expectedPrice you sent differs from the current price; the order was NOT created. details.current holds the current price. Refresh the catalog and retry with it. no
422 OUT_OF_STOCK Not enough stock (waitForStock=false was sent). Retry later, or send waitForStock=true to let the order wait in queue. yes
422 INVALID_FIELDS Required fields are missing or violate the length constraint. details.fields lists the offending fields. Read the schema from GET /catalog/products/{productId}. no
422 INVALID_PLAYER_ID The player information could not be validated by the supplier. Check with POST /orders/validate before ordering; ask the customer for the correct id. no
422 LIMIT_EXCEEDED Daily purchase limit or per-item quantity cap exceeded. See the limits object in GET /me; the daily limit resets at midnight. no
422 PRODUCT_DISABLED The product is not on sale or has no price defined. Refresh the catalog; you will get a product.enabled event when it is back. no
422 PRODUCT_NOT_ALLOWED The product cannot be sold through the dealer channel. Such products are not listed by the catalog endpoints either; drop them from the body. no
422 MIN_QTY Below the product minimum order quantity. Use limits.minQty from the catalog. no
409 IDEMPOTENCY_MISMATCH The same Idempotency-Key was previously used with a DIFFERENT body. Generate a new key per new order (your order number plus an attempt counter works well). no
409 ORDER_IN_PROGRESS An order with the same key is being processed right now. Wait a few seconds and read the result with GET /orders/by-ref/{externalRef}. yes
422 PRODUCT_NOT_ALLOWED The product cannot be sold through the dealer channel. Such products are not listed by the catalog endpoints either; drop them from the body. no
GET /bayi-api/v1/orders/{orderId} scope: orders:read

Order detail and codes #

Order detail plus delivered codes. Codes are returned ONLY here and in the order response; they are never sent inside a webhook payload. Test-mode orders have ids like "test_12" and are read from the same endpoint. Another dealer's order returns 404.

Name In Type Required Description
orderId path string yes Order number, or a test order id (test_12).
Example request
curl "https://oyuneks.com/bayi-api/v1/orders/184213" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "orderId": 184213,
        "externalRef": "SIP-2026-0001",
        "channel": "dealer-api",
        "status": "partially_delivered",
        "mode": "live",
        "total": "608.90",
        "balanceAfter": "47641.10",
        "createdAt": "2026-08-28T14:05:00+03:00",
        "items": [
            {
                "itemId": "184213-1",
                "productId": 6,
                "name": "Knight Online 1020 Cash",
                "quantity": 2,
                "unitPrice": "98.70",
                "status": "delivered",
                "codes": [
                    {
                        "code": "KO10-2XQ4-9WPA",
                        "serial": null,
                        "expiresAt": null
                    }
                ],
                "receipt": null,
                "failCode": null,
                "failReason": null
            },
            {
                "itemId": "184213-2",
                "productId": 1421,
                "name": "PUBG Mobile 660 UC",
                "quantity": 1,
                "unitPrice": "411.50",
                "status": "failed",
                "codes": [],
                "receipt": null,
                "failCode": "TIMEOUT",
                "failReason": "ZAMAN_ASIMI: stok 15 dk icinde saglanamadi"
            }
        ]
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
404 NOT_FOUND Record or endpoint not found. Verify the id and the path. Another dealer's order also returns "not found" (we do not leak existence). no
GET /bayi-api/v1/orders/by-ref/{externalRef} scope: orders:read

Find an order by your own reference #

Finds an order by your own reference (the LATEST record if the reference repeats). This is the fastest answer to "did my order go through" after a network error. Test orders are found here too.

Name In Type Required Description
externalRef path string yes The externalRef you sent in the order body (1-64 chars, no slash).
Example request
curl "https://oyuneks.com/bayi-api/v1/orders/by-ref/SIP-2026-0001" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "orderId": 184213,
        "externalRef": "SIP-2026-0001",
        "channel": "dealer-api",
        "status": "delivered",
        "mode": "live",
        "total": "608.90",
        "balanceAfter": "47641.10",
        "createdAt": "2026-08-28T14:05:00+03:00",
        "items": []
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
404 NOT_FOUND Record or endpoint not found. Verify the id and the path. Another dealer's order also returns "not found" (we do not leak existence). no
GET /bayi-api/v1/orders scope: orders:read

Order list (cursor paged) #

Your orders; panel and API orders live in the same list (same ledger) and can be split with channel. The productId filter is applied ONLY to the returned page, because order contents live inside a JSON snapshot and cannot be queried in SQL. In that case the response carries a note field, so the bot does not conclude "no such product".

Name In Type Required Description
from query string no Start date (inclusive).
to query string no End date (inclusive).
status query string
processing | delivered | partially_delivered | cancelled | failed
no Status filter. An invalid value returns 400 (it is not silently ignored).
channel query string
dealer-api | dealer-panel
no Channel filter. Left empty, both channels are returned.
productId query integer no Filters within the returned page only; continue with nextCursor for further pages.
cursor query string no The nextCursor from the previous response (last order id).
limit query integer
default: 25
no Page size (1-100).
Example request
curl "https://oyuneks.com/bayi-api/v1/orders?from=2026-08-01&to=2026-08-28" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "items": [
            {
                "orderId": 184213,
                "externalRef": "SIP-2026-0001",
                "channel": "dealer-api",
                "status": "delivered",
                "mode": "live",
                "total": "608.90",
                "balanceAfter": "47641.10",
                "createdAt": "2026-08-28T14:05:00+03:00",
                "items": [
                    {
                        "itemId": "184213-1",
                        "productId": 6,
                        "name": "Knight Online 1020 Cash",
                        "quantity": 2,
                        "unitPrice": "98.70",
                        "status": "delivered",
                        "codes": [],
                        "receipt": null,
                        "failCode": null,
                        "failReason": null
                    }
                ]
            }
        ],
        "nextCursor": "184213",
        "count": 1
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
POST /bayi-api/v1/orders/{orderId}/cancel scope: orders:write

Cancel pending items #

Cancels only PENDING (processing) items and refunds them. Delivered items are untouched: you already hold the code, refunding an unused code is a support decision. The refund goes through the canonical delivery path; this endpoint does not open a second refund route.

Name In Type Required Description
orderId path integer yes Order number (digits only; test orders cannot be cancelled).

Request body

Field Type Required Description
reason string no Cancellation reason (stored in the audit trail, not shown to customers).
Example request
curl -X POST "https://oyuneks.com/bayi-api/v1/orders/184219/cancel" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Müşteri vazgeçti"}'
Example response - HTTP 200
{
    "data": {
        "orderId": 184219,
        "externalRef": "SIP-2026-0007",
        "channel": "dealer-api",
        "status": "cancelled",
        "mode": "live",
        "total": "41.90",
        "balanceAfter": "48250.00",
        "createdAt": "2026-08-28T14:20:00+03:00",
        "items": [
            {
                "itemId": "184219-1",
                "productId": 1421,
                "name": "PUBG Mobile 660 UC",
                "quantity": 1,
                "unitPrice": "41.90",
                "status": "cancelled",
                "codes": [],
                "receipt": null,
                "failCode": "CANCELLED",
                "failReason": "Müşteri vazgeçti"
            }
        ]
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
404 NOT_FOUND Record or endpoint not found. Verify the id and the path. Another dealer's order also returns "not found" (we do not leak existence). no
422 NOT_CANCELLABLE A goldbar item cannot be cancelled through this endpoint; it is delivered by hand. Contact support to cancel. E-pin/top-up items in the same order are cancelled as usual. no

Reports

Statement and invoices. The panel statement, the API statement and the invoice show the SAME numbers.

GET /bayi-api/v1/reports/statement scope: balance:read

Period statement (json/csv) #

Opening balance, deposit/order/refund totals, closing balance and the entry rows. format=csv returns a file you can hand to an accounting tool (with BOM, semicolon separated, no envelope). If the 5000-row cap is hit a note appears; narrow the range.

Name In Type Required Description
from query string
default: ayın 1'i
no Start date. Defaults to the first day of the current month.
to query string
default: bugün
no End date. Returns 400 when from > to.
format query string
json | csv
default: json
no With csv you get a file download (text/csv).
Example request
curl "https://oyuneks.com/bayi-api/v1/reports/statement?from=2026-08-01&to=2026-08-28" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "from": "2026-08-01",
        "to": "2026-08-28",
        "currency": "TRY",
        "openingBalance": "12500.00",
        "deposits": "75000.00",
        "orders": "39250.00",
        "refunds": "0.00",
        "other": "0.00",
        "closingBalance": "48250.00",
        "count": 2,
        "items": [
            {
                "id": 3012301,
                "date": "2026-08-28T09:12:00+03:00",
                "typeId": 2,
                "amount": "25000.00",
                "balanceAfter": "48250.00",
                "orderId": null,
                "description": "Havale"
            },
            {
                "id": 3012402,
                "date": "2026-08-28T14:05:00+03:00",
                "typeId": 1,
                "amount": "-608.90",
                "balanceAfter": "47641.10",
                "orderId": 184213,
                "description": "Sipariş ödemesi"
            }
        ]
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
GET /bayi-api/v1/invoices scope: balance:read

Invoice list #

Issued invoices: number, date, total, VAT and the panel URL. A PDF/UBL download link is NOT exposed through the API yet (pdfUrl is null): the on-site invoice view is session based, and minting signed short-lived links would open a new authorization surface that is out of scope for this version. Use panelUrl to view the document.

Name In Type Required Description
from query string no Start date (inclusive).
to query string no End date (inclusive).
Example request
curl "https://oyuneks.com/bayi-api/v1/invoices?from=2026-08-01&to=2026-08-28" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "items": [
            {
                "invoiceId": 90124,
                "number": "OYN2026000012345",
                "numbers": [
                    "OYN2026000012345"
                ],
                "date": "2026-08-01T10:00:00+03:00",
                "type": "satis",
                "orderNo": "184213",
                "total": "608.90",
                "vat": "0.00",
                "currency": "TRY",
                "pdfUrl": null,
                "panelUrl": "/faturalarim/90124"
            }
        ],
        "count": 1,
        "note": "PDF indirme baglantisi henuz API uzerinden verilmiyor; belge panelUrl adresinden (oturumlu) goruntulenir."
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Webhooks

Stop polling: order results, price changes and balance credits arrive as signed events. There is ONE address per dealer, so PUT /webhooks is an upsert.

GET /bayi-api/v1/webhooks scope: webhooks:manage

Subscription status #

Address, subscribed events, status (active|suspended), last success/failure stamps, consecutive failure counter and the MASKED signing secret. The raw secret is never returned here: if whoever can read the token could also read the secret, the signature would be pointless. The response also carries the catalogue of subscribable events.

Example request
curl "https://oyuneks.com/bayi-api/v1/webhooks" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "configured": true,
        "webhook": {
            "id": 214,
            "url": "https://bayi.example.com/oyuneks-hook",
            "events": [
                "order.delivered",
                "order.failed",
                "price.changed",
                "balance.low"
            ],
            "status": "active",
            "secretMasked": "whsec_a91f3c********9d0e",
            "failStreak": 0,
            "lastSuccessAt": "2026-08-28T14:05:00+03:00",
            "lastFailureAt": null
        },
        "availableEvents": {
            "order.delivered": "Siparis teslim edildi (kismi teslim de bu olayla gelir)"
        }
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}
GET /bayi-api/v1/webhooks/deliveries scope: webhooks:manage

Delivery log (cursor paged) #

Event name, channel (webhook|email), status (pending|sent|dead), attempt count, HTTP code, duration and error text. The event BODY is not returned. Re-sending is currently only available from the dealer panel. The retry table is included in the response.

Name In Type Required Description
cursor query string no The nextCursor from the previous response.
limit query integer
default: 50
no Page size (1-200).
Example request
curl "https://oyuneks.com/bayi-api/v1/webhooks/deliveries?limit=50" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "items": [
            {
                "id": 9214,
                "eventId": "evt_o184213_d",
                "type": "order.delivered",
                "channel": "webhook",
                "mode": "live",
                "status": "sent",
                "attempts": 1,
                "httpCode": 200,
                "durationMs": 184,
                "error": null,
                "createdAt": "2026-08-28T14:05:01+03:00",
                "nextAttemptAt": null,
                "sentAt": "2026-08-28T14:05:02+03:00"
            }
        ],
        "nextCursor": "9214",
        "count": 1,
        "retry": {
            "maxAttempts": 8,
            "backoffMinutes": [
                2,
                4,
                8,
                16,
                32,
                64,
                128
            ],
            "note": "Tavan 720 dakikadir. 8 denemede cevap alinamazsa olay olu sayilir."
        }
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}
PUT /bayi-api/v1/webhooks scope: webhooks:manage

Create or update the subscription #

One address per dealer, so this is an upsert. https only; localhost and private ranges are rejected (SSRF). On the FIRST save the signing secret is returned ONCE and never shown again. Changing the address lifts an existing suspension. An unknown event name is not dropped silently but reported with 400. Sending an empty events list applies the default set. POST /webhooks maps to the same handler.

Aliases (same handler): POST /bayi-api/v1/webhooks

Request body

Field Type Required Description
url string yes An https URL. Redirects (3xx) are NOT followed, write the final address.
events array no Event names. Empty applies the default set (order.delivered, order.failed, price.changed, balance.low).
Example request
curl -X PUT "https://oyuneks.com/bayi-api/v1/webhooks" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://bayi.example.com/oyuneks-hook","events":["order.delivered","order.failed","price.changed","balance.low"]}'
Example response - HTTP 200
{
    "data": {
        "webhook": {
            "id": 214,
            "url": "https://bayi.example.com/oyuneks-hook",
            "events": [
                "order.delivered",
                "order.failed",
                "price.changed",
                "balance.low"
            ],
            "status": "active",
            "secretMasked": "whsec_a91f3c********9d0e",
            "failStreak": 0,
            "lastSuccessAt": null,
            "lastFailureAt": null
        },
        "signature": {
            "header": "X-Oyuneks-Signature",
            "format": "t=<unix>,v1=<hex>",
            "algorithm": "HMAC-SHA256"
        },
        "secret": "whsec_a91f3c7d2b8e4f16a05c9d0e",
        "secretNote": "Bu sir yalniz bu yanitta doner."
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
POST /bayi-api/v1/webhooks/rotate-secret scope: webhooks:manage

Rotate the signing secret #

Generates a new signing secret and returns it ONCE. The old secret dies immediately: keeping two valid secrets would make revoking a leaked one pointless. Events arriving before you deploy the new secret cannot be verified, so roll it out right away.

Example request
curl -X POST "https://oyuneks.com/bayi-api/v1/webhooks/rotate-secret" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "secret": "whsec_2f7c81a94de0b3c65a1f8d42",
        "secretNote": "Eski sir ANINDA gecersizdir.",
        "webhook": {
            "id": 214,
            "url": "https://bayi.example.com/oyuneks-hook",
            "events": [
                "order.delivered"
            ],
            "status": "active",
            "secretMasked": "whsec_2f7c81********8d42"
        },
        "signature": {
            "header": "X-Oyuneks-Signature",
            "format": "t=<unix>,v1=<hex>",
            "algorithm": "HMAC-SHA256"
        }
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
POST /bayi-api/v1/webhooks/test scope: webhooks:manage

Send a test event (ping) #

Sends the "ping" event IMMEDIATELY (not queued, not retried) and returns the HTTP result. If your address does not answer the response is STILL 200 and delivered=false in the body: the request was handled correctly, the delivery failed. Returning 502 would make the bot think its own request was wrong. POST /webhooks/{webhookId}/test maps to the same handler.

Aliases (same handler): POST /bayi-api/v1/webhooks/{webhookId}/test

Example request
curl -X POST "https://oyuneks.com/bayi-api/v1/webhooks/test" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "delivered": true,
        "eventId": "evt_01K3QF6ZC2M9V0X7NB4TDHR8YE",
        "httpCode": 200,
        "durationMs": 184,
        "error": null
    },
    "mode": "test",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
400 BAD_REQUEST Malformed request: missing field, invalid value, or no Idempotency-Key. Read the message and fix the body; repeating the same request returns the same error. no
DELETE /bayi-api/v1/webhooks scope: webhooks:manage

Delete the subscription #

Deletes the subscription; queued events are marked dead. DELETE /webhooks/{webhookId} is accepted too; an id owned by another dealer returns 404. Narrowing the event list is usually better than deleting: without an address you also lose delivery notifications.

Aliases (same handler): DELETE /bayi-api/v1/webhooks/{webhookId}

Example request
curl -X DELETE "https://oyuneks.com/bayi-api/v1/webhooks" \
  -H "Authorization: Bearer oyxb_test_KENDI_ANAHTARIN"
Example response - HTTP 200
{
    "data": {
        "deleted": true
    },
    "mode": "live",
    "requestId": "01K3QF6ZC2M9V0X7NB4TDHR8YE"
}

Endpoint specific errors

HTTP code Meaning Retry
404 NOT_FOUND Record or endpoint not found. Verify the id and the path. Another dealer's order also returns "not found" (we do not leak existence). no

Connect via MCP

MCP (Model Context Protocol) is the open protocol AI clients use to connect to a system through tools: you connect your Oyuneks dealer account to a client like Claude and get work done without writing code.

One address: /bayi-api/mcp (JSON-RPC 2.0, stateless). Authentication uses the SAME key as /bayi-api/v1; the mode comes from the key prefix (oyxb_test_ / oyxb_live_) and every tool result carries a "mode" field. Tools are a thin translator on top of this API: each call goes to the real /bayi-api/v1 endpoint with the same scopes, rate limits and audit trail.

The model cannot invent prices: product ids, prices and required fields are read from the catalog tools. The two money tools (place order, cancel order) are TWO-STEP: the first call does nothing and returns a summary (items, quantity, unit and total price, balance after, mode) plus a five minute confirmation token; the tool is called again with that token only after you approve.

Delivered codes are MASKED in tool results (last four characters) and revealed only on request. Repeat protection uses an Idempotency-Key derived from the confirmation token: repeating the same confirmation returns the same order, never a second one.

Claude Code (terminal)
claude mcp add --transport http oyuneks-bayi https://oyuneks.com/bayi-api/mcp --header "Authorization: Bearer <anahtar>"
Tool Endpoint Scope Two-step What it does
bayi_bilgim GET /bayi-api/v1/me (herhangi) + balance:read no Dealer identity, company, tier, available balance, daily limit and the key mode (live/test).
katalog_ara GET /bayi-api/v1/catalog/products catalog:read no Searches products: productId, dealer price, stock, required-field schema and quantity limits.
fiyat_degisiklikleri GET /bayi-api/v1/catalog/prices catalog:read no Products whose price, stock or availability changed since a given time (price sync).
hesap_dogrula POST /bayi-api/v1/orders/validate orders:write no Validates the player account with the supplier for top-up products and returns the nickname.
siparis_ver POST /bayi-api/v1/orders orders:write yes Places an order. TWO-STEP: a summary plus a confirmation token first, the order only after approval.
siparislerim GET /bayi-api/v1/orders orders:read no Lists orders (status, channel, date filters). Codes are always masked in this list.
siparis_detayi GET /bayi-api/v1/orders/{siparisNo} orders:read no One order in detail, by order number or your own reference. Codes can be revealed on request.
siparis_iptal POST /bayi-api/v1/orders/{siparisNo}/cancel orders:write yes Cancels pending items and refunds them to the balance. Requires the TWO-STEP confirmation.
ekstre GET /bayi-api/v1/reports/statement balance:read no Period statement: opening/closing balance, deposits, orders, refunds and the ledger rows.
webhook_ayarim GET /bayi-api/v1/webhooks webhooks:manage no Shows the webhook subscription (address, events, state). Changing it is done in the panel.
webhook_test POST /bayi-api/v1/webhooks/test webhooks:manage no Sends a signed ping event to the registered webhook address and returns the result.

State machine

Item state and order state are separate: delivery and cancellation happen per item, the order state is derived from them. A pending item that cannot be delivered within 15 minutes is automatically failed and refunded (failCode=TIMEOUT).

Order Item Meaning Final?
processing processing Paid, delivery in progress: waiting for stock or being pulled from the supplier. Returned with HTTP 202. no
delivered delivered All items delivered (a code for e-pin, a receipt for top-up). yes
partially_delivered — Some items delivered, others cancelled/refunded. The refund appears as a refund row in the ledger. yes
cancelled cancelled Pending items were cancelled and refunded (by you or by support). yes
failed failed Timed out: stock could not be supplied in time, the amount was refunded. Item carries failCode=TIMEOUT. yes

Event catalogue

Register an https address and an event list (from the panel or with the webhooks:manage scope). Every event is POSTed to you with a signature.

Event When
order.delivered Order delivered (a partial delivery arrives with this event too)
order.failed Order failed or cancelled: the amount was refunded to your balance
price.changed Your dealer price changed
stock.changed The stock state of a product changed
product.enabled The product is on sale again
product.disabled The product went off sale
balance.low Your balance dropped below your threshold
balance.credited Your balance was credited
ping Sent when you press "test" in the panel or call the test endpoint. No subscription needed, not queued, not retried. The mode field is "test".

Headers we send

Header Value
X-Oyuneks-Event Olay adı (order.delivered, price.changed, ping...).
X-Oyuneks-Event-Id Olay kimliği. AYNI KİMLİK İKİ KEZ GELEBİLİR (ağ hatası sonrası yeniden deneme); ikinci gelişi yok sayın.
X-Oyuneks-Signature t=<unix saniye>,v1=<hex HMAC-SHA256>
User-Agent Oyuneks-Bayi-Webhook/1.0
Content-Type application/json
Example event - order.delivered
{
    "id": "evt_o184213_d",
    "type": "order.delivered",
    "mode": "live",
    "createdAt": "2026-08-28T14:05:02+03:00",
    "data": {
        "orderId": 184213,
        "externalRef": "SIP-2026-0001",
        "channel": "dealer-api",
        "status": "delivered",
        "total": "197.40",
        "currency": "TRY",
        "createdAt": "2026-08-28T14:05:00+03:00",
        "items": [
            {
                "itemId": "184213-1",
                "productId": 6,
                "name": "1020 Cash",
                "quantity": 2,
                "status": "delivered",
                "unitPrice": "98.70",
                "codeCount": 2
            }
        ]
    }
}

Signature

Verify the signature - PHP
<?php
// HAM govde sart: json_decode + json_encode ile imza TUTMAZ.
$ham = file_get_contents("php://input");
$imza = $_SERVER["HTTP_X_OYUNEKS_SIGNATURE"] ?? "";

$t = null; $v1 = null;
foreach (explode(",", $imza) as $parca) {
    list($ad, $deger) = array_pad(explode("=", trim($parca), 2), 2, null);
    if ($ad === "t") { $t = $deger; }
    if ($ad === "v1") { $v1 = $deger; }
}

// 5 dakikadan eski istegi reddet (tekrar oynatma korumasi).
if ($t === null || abs(time() - (int) $t) > 300) { http_response_code(400); exit; }

$beklenen = hash_hmac("sha256", $t . "." . $ham, "whsec_SIZIN_SIRRINIZ");
if (!hash_equals($beklenen, (string) $v1)) { http_response_code(401); exit; }

$olay = json_decode($ham, true);
// Ayni olay iki kez gelebilir: id ile tekillestir, isi ASENKRON yap.
kuyrugaAl($olay["id"], $olay["type"], $olay["data"]);
http_response_code(200);
Verify the signature - Node.js
import crypto from "node:crypto";

// express: app.post("/oyuneks-hook", express.raw({ type: "application/json" }), ...)
function dogrula(hamGovde, imzaBasligi, sir) {
  const parcalar = Object.fromEntries(
    String(imzaBasligi).split(",").map((p) => p.trim().split("="))
  );
  const t = Number(parcalar.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const beklenen = crypto
    .createHmac("sha256", sir)
    .update(t + "." + hamGovde)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(beklenen), Buffer.from(parcalar.v1));
}

Your response and retries

TESLIM EDILEN KODLAR WEBHOOK GOVDESINDE GONDERILMEZ. Kalemde yalnızca codeCount (kod adedi) bulunur; kodlar GET /bayi-api/v1/orders/{orderId} ucundan çekilir.
Yalnız https. localhost, 127.0.0.1 ve özel ağ adresleri (10.x, 192.168.x, 172.16-31.x, 169.254.x) kabul edilmez.
Webhook olayları YALNIZ bayi kanalından (panel ya da API) verilen siparişler için üretilir; sitede normal müşteri olarak verdiğiniz siparişler olay üretmez.

Reconciliation

Webhooks are a helper, NOT the source of truth. At end of day pull the ledger with GET /bayi-api/v1/balance/transactions: every order row carries an orderId and, when set, your externalRef, and balanceAfter lets you verify the balance chain.

The same event may arrive more than once (retry after a network error): deduplicate by the id field. Ordering is not guaranteed; read the FINAL state of an order from GET /bayi-api/v1/orders/{orderId}.

The panel statement, the API statement and the invoice show the same numbers. If you see a one-kurus difference, open a support ticket with the X-Request-Id.

Versioning & changelog

The version lives in the URL (/v1). Adding a field is NOT a breaking change: write your client so unknown fields are ignored. Removing a field or changing its meaning happens in a new version, and the old one lives at least six months.

Changes are announced here with dates; machines can follow the info.version field of openapi.json.

28.08.2026 · v1 · Dealer API is live

This document is public: oyuneks.com/bayi-api/docs. Machine readable: openapi.json, postman.json, llms.txt.