GamesDrop API Documentation
# GamesDrop API — Official Documentation (English)
## 1. Overview
The API connects bots, applications, and partner stores to GamesDrop. All responses use JSON.
- Base URL: `https://gamesdrop.org/api/v1`
- Authentication: `Authorization: Bearer GD_API_KEY` (the key looks like `gd_live_xxxxxxxxxxxx.secret`)
- Content-Type: `application/json`
- Send requests from your server. Never expose the API key in a browser or client-side application.
- Order creation requires an `Idempotency-Key` header: 8–128 printable ASCII characters (a UUID is recommended). Use a new key for every new order. Keep the same key only when safely retrying the exact same request.
- Every response includes an `X-Request-Id` header. Send it to support when reporting a problem.
- The `error` text is human-readable and may be in Tajik. Always build your logic on the `code` field and the HTTP status, never on the text.
## 2. Get an API key
Open the **API** section in your GamesDrop account and create a key. The full value is displayed only once. Store it in your server environment. If it is exposed, revoke it and create a replacement.
## 3. Catalog and pricing
Category IDs (`rg-...`) and offer IDs (`rgp-...`) are opaque strings. Always take them from the catalog; do not build or guess them. The `categoryId` and `offerId` values in the examples below are placeholders.
### `GET /catalog`
Returns active categories. Optional query parameters: `q`, `limit` (1–100, default 50), and `offset`.
```bash
curl 'https://gamesdrop.org/api/v1/catalog?limit=50' \
-H 'Authorization: Bearer GD_API_KEY'
```
Example response:
```json
{"ok":true,"total":1,"categories":[{"id":"rg-XXXX","kind":"topup","title":"Free Fire — SG"}]}
```
### `GET /catalog/{id}`
Returns category details, required input fields, offers, and prices. `priceTjs` is your partner price. `retailPriceTjs` is the suggested retail price and may be `null`. Prices may change, so refresh them before placing an order.
The `fields` list shows exactly which keys you must send in `fields` when you check an ID or create an order (for example `gameUserId`). A missing key returns `400 missing_field`.
```json
{"ok":true,"category":{"id":"rg-XXXX","kind":"topup","title":"Free Fire — SG","fields":[{"key":"gameUserId","label":"Player ID","type":"text","options":null}],"idCheckField":"gameUserId","offers":[{"offerId":"rgp-XXXX","title":"110 Diamonds","priceTjs":"8.75","retailPriceTjs":null}],"steamTopup":null}}
```
`steamTopup` is `null` for ordinary categories. For a category that supports Steam top-up by amount it is an object:
- `minUsd`, `maxUsd` — allowed amount range in USD
- `tjsPerUsd` — your rate in TJS per 1 USD (already includes your price level)
- `noFeeFromUsd`, `tjsPerUsdNoFee` — optional; a better rate for amounts from `noFeeFromUsd`
## 4. Player ID check
### `POST /check-id`
Request (use the key from `idCheckField`):
```json
{"categoryId":"rg-XXXX","fields":{"gameUserId":"123456789"}}
```
Example response:
```json
{"ok":true,"valid":true,"playerName":"PlayerOne","region":"SG"}
```
`valid:true` means the ID is confirmed. `valid:false` means the ID was not confirmed: it is wrong, belongs to another region, or the game cannot be verified. `503 check_unavailable_now` means the check is temporarily unavailable; it does not mean the ID is invalid.
## 5. Create an order
### `POST /orders`
Headers:
```http
Authorization: Bearer GD_API_KEY
Content-Type: application/json
Idempotency-Key: NEW-UUID
```
Standard product:
```json
{"categoryId":"rg-XXXX","offerId":"rgp-XXXX","fields":{"gameUserId":"123456789"}}
```
Steam top-up (only for categories where `steamTopup` is not `null`; otherwise `400 invalid_request`):
```json
{"categoryId":"STEAM_CATEGORY_ID","amountUsd":20,"fields":{"login":"steamlogin"}}
```
`amountUsd` is a number (not a string), greater than 0, with at most 2 decimals, within `minUsd`–`maxUsd`. Do not send `offerId` together with `amountUsd`. In the response of a Steam order `offerId` is `null` and `amountUsd` is returned. The exact price is in `order.priceTjs`; it may differ from `amountUsd × tjsPerUsd` by up to 0.01 TJS because of rounding.
Example response:
```json
{"ok":true,"order":{"id":"UUID","number":128,"categoryId":"rg-XXXX","offerId":"rgp-XXXX","priceTjs":"8.75","status":"processing","delivery":null,"error":null,"createdAt":"2026-10-11T10:00:00Z","completedAt":null}}
```
The order price is deducted from the GamesDrop internal balance. If funds are insufficient, the API returns `402 insufficient_balance` and no order is created.
Idempotency: repeating a request with the same `Idempotency-Key` and the same body returns the original order (or the original error) and never creates a second order. If the first request is still being processed you get `409 in_progress`; wait a few seconds and repeat. The same key with a different body returns `422 idempotency_key_reuse`.
If the provider fails to fulfil the order, the API returns `502 fulfillment_failed` with the order in the `order` field. The funds are returned and the order status is `refunded`.
## 6. Retrieve orders
- `GET /orders/{id}` — retrieve an order by UUID or short order number.
- `GET /orders?status=processing&limit=50&offset=0` — list orders (newest first) with filtering and pagination. `limit` is 1–100 (default 50). The response contains `total` and `orders`.
Statuses: `processing` (in progress), `fulfilled` (completed), `refunded` (the provider failed and the funds were returned), and `failed` (the order failed; see the `error` field). Do not rely on `processing` ending quickly: use webhooks or poll `GET /orders/{id}`.
## 7. Balance
### `GET /balance`
```json
{"ok":true,"balanceTjs":"125.40"}
```
This is your GamesDrop account balance. All amounts are shown in Tajik somoni (TJS) with two decimal places.
## 8. Webhooks
Configure a webhook in your account → **API**, or through the API. The URL must meet these rules: `https://` only, port 443 only, no login or password in the URL, and the host must resolve to a public IPv4 address (private, local, and IPv6-only hosts are rejected with `400 invalid_request`). Redirects are not followed.
- `PUT /webhook` — save a URL: `{"url":"https://example.com/hook"}`. The response shows the full secret (`whsec_...`).
- `GET /webhook` — retrieve settings (the secret is partly hidden), the time and result of the last delivery, and `failsInARow`.
- `POST /webhook` — management actions. Rotate the secret with `{"action":"rotate_secret"}`; the response shows the new secret.
- Disable it with `PUT /webhook` and `{"url":null}`.
Events: `order.fulfilled`, `order.refunded`, and `order.failed`.
Request body:
```json
{"event":"order.fulfilled","sentAt":"2026-10-11T10:00:05Z","order":{"id":"UUID","number":128,"status":"fulfilled","...":"same fields as GET /orders/{id}"}}
```
Signature header: `X-GamesDrop-Signature: t=UNIX,v1=HEX`. The signature is HMAC-SHA256 of `t + "." + RAW_BODY` (the raw request body, byte for byte), using your webhook secret. Compare in constant time. Reject events whose `t` is older than five minutes.
Delivery: your endpoint must answer with HTTP 2xx within 8 seconds. Otherwise GamesDrop retries: up to 4 attempts in total, with pauses of 1, 5, and 15 minutes. An HTTP 4xx answer is treated as final and is not retried. Every attempt is signed with a fresh `t`. Respond quickly and deduplicate repeated events using `order.id` and `event`. If something is missed, read the order with `GET /orders/{id}`.
## 9. Errors and rate limits
Example error:
```json
{"ok":false,"code":"insufficient_balance","error":"Дар ҳисоби панел маблағ нокифоя аст."}
```
The `error` text is for people and may change. Use `code`. Some errors have extra fields, for example `field` in `missing_field` and `retryAfterSeconds` in `rate_limited`.
| HTTP | Code | Meaning |
|---|---|---|
| 400 | `invalid_request`, `missing_field` | Invalid request or missing field (`field` names it) |
| 401 | `unauthorized`, `key_revoked` | Invalid or revoked API key |
| 402 | `insufficient_balance` | Insufficient balance |
| 403 | `account_blocked` | Account is blocked |
| 404 | `not_found` | Category, offer, or order not found |
| 405 | `method_not_allowed` | Method not allowed for this path |
| 409 | `offer_unavailable`, `in_progress` | Offer unavailable or request still processing |
| 422 | `idempotency_key_reuse` | An idempotency key was reused with a different payload |
| 429 | `rate_limited` | Rate limit exceeded; retry later |
| 500 | `internal_error` | Internal error; send `requestId` to support |
| 502 | `fulfillment_failed` | Provider failed; funds are refunded; the response contains `order` |
| 503 | `service_unavailable`, `check_unavailable_now` | Service temporarily unavailable |
The default limit is 30 requests per minute per key (administrators can change it). On `429`, wait for `retryAfterSeconds` (also sent in the `Retry-After` header) before retrying. `429` can also mean the service is busy for all partners at the moment; the same rule applies.
## 10. Security
1. Never share your API Key or Webhook Secret or commit them to GitHub.
2. Use HTTPS only.
3. Make API requests from your server, not from a browser.
4. Use a new `Idempotency-Key` for each new order.
5. Verify the product price and player ID before ordering.
6. Verify the webhook signature on every event before you trust it.