API reference
Every endpoint the Warya reseller API publishes, the scope each one needs, the parameters it accepts, and the exact envelope every answer carries. Readable without an account.
https://api.waryashop.org/api/v1
Authentication
Send your key in the X-API-Key request header on every call. Never in a URL or query string. A missing, unknown, disabled or revoked key answers 401 and names nothing else about the account. An account holds 1 key; it carries scopes and may restrict the addresses it answers from (up to 32 rules).
curl "https://api.waryashop.org/api/v1/balance" \
-H "X-API-Key: YOUR_API_KEY"Rate limits and pagination
| Where | Per caller | Whole deployment |
|---|---|---|
| Any /api/v1 request, before the key is checked | 600/min | 6,000/min |
| Your key (per key, configurable) | 60/min (default, up to 6,000) | — |
POST https://api.waryashop.org/api/v1/player/validate |
60/min | 600/min |
Every answer carries X-RateLimit-Limit and X-RateLimit-Remaining; a 429 also carries Retry-After and X-RateLimit-Reset, and repeats the wait as meta.retry_after. Listing endpoints are paged with page and per_page (alias limit): per_page is clamped to 1..100 on the order and transaction listings, while the catalogue listings default to and are capped at 500 families per page, with meta.has_more set when there are more.
Catalog
https://api.waryashop.org/api/v1/games
catalog:read
List games and products
The code, English name, optional Persian name and Warya-hosted image URL. Paged; the default page is the whole list.
- Success:
- 200
- Scope:
catalog:read- Query:
- search, category, page, per_page, sort
https://api.waryashop.org/api/v1/games/{code}/catalogue
catalog:read
Packages and sale prices
Use the exact catalogue_name returned here when creating an order. These are Warya sale prices in USD, as decimal strings. Only packages that can be priced right now are listed: an item that is no longer on sale, or one whose amount cannot be confirmed, is absent here rather than published at 0.000000, and naming it in an order is a 422.
- Success:
- 200
- Scope:
catalog:read- Path:
- code
- Query:
- search, in_stock, page, per_page, sort
https://api.waryashop.org/api/v1/games/{code}/fields
catalog:read
Required player fields
The identifiers this game's order form must collect, with meta.source and meta.freshness saying where the answer came from. Vouchers return an empty list.
- Success:
- 200
- Scope:
catalog:read- Path:
- code
https://api.waryashop.org/api/v1/games/{code}/servers
catalog:read
Servers and regions
An empty list is a verified answer: this game needs no server selection. A game whose list cannot be confirmed is a 503, never an empty list.
- Success:
- 200
- Scope:
catalog:read- Path:
- code
Checkout
https://api.waryashop.org/api/v1/player/validate
players:validate
Validate player
Confirm an account before charging your customer. A temporary failure is a 503 with Retry-After, never "invalid account".
- Success:
- 200
- Scope:
players:validate- Body:
- game, player_id, server_id?, charname?
https://api.waryashop.org/api/v1/orders
orders:write
Create order
Confirms the current Warya price, charges your wallet and starts fulfillment. If the price cannot be confirmed, the answer is a 503 and nothing is charged.
- Success:
- 201
- Scope:
orders:write- Header:
- Idempotency-Key
- Body:
- game, catalogue_name, player_id, server_id?, charname?, customer_reference?, callback_url?
Requires the Idempotency-Key header. Here is the 201 it answers with:
{
"success": true,
"order_id": "WR-20261010-8F3KD",
"status": "processing",
"charged": "0.950000",
"currency": "USD",
"idempotent_replay": false,
"data": {
"order_id": "WR-20261010-8F3KD",
"status": "processing",
"charged": "0.950000",
"currency": "USD",
"idempotent_replay": false
},
"meta": {
"request_id": "01J8ZQ4M6W9K2N7P3R5T8V0XYZ"
}
}https://api.waryashop.org/api/v1/orders/{orderId}
orders:read
Get order status
For completed vouchers, delivery contains the redeem code.
- Success:
- 200
- Scope:
orders:read- Path:
- orderId
https://api.waryashop.org/api/v1/orders
orders:read
List orders
Paginated order history; newest first.
- Success:
- 200
- Scope:
orders:read- Query:
- status, type, game, customer_reference, from, to, page, per_page, sort
Account
https://api.waryashop.org/api/v1/balance
balance:read
Wallet balance
Available and locked USD balances are returned as decimal strings.
- Success:
- 200
- Scope:
balance:read
https://api.waryashop.org/api/v1/transactions
transactions:read
Wallet transactions
Paginated wallet ledger for reconciliation.
- Success:
- 200
- Scope:
transactions:read- Query:
- type, status, from, to, page, per_page, sort
Status and error contract
Success answers success: true with the endpoint keys at the top level, the same keys under data, and meta.request_id (also in the X-Request-Id header). A failure answers success: false with error_code and message, repeating them under error.
| Status | Codes | Meaning |
|---|---|---|
| 200 / 201 | — |
Success. success: true, the endpoint's own keys, the same keys under data, and meta.request_id. |
| 400 | BAD_REQUEST / IDEMPOTENCY_KEY_REQUIRED |
The request could not be understood, or a key that requires an Idempotency-Key did not send one. |
| 401 | MISSING_API_KEY / INVALID_API_KEY / UNAUTHENTICATED |
No key, an unknown key, or a disabled or revoked key. Nothing else about the account is disclosed. |
| 402 | INSUFFICIENT_BALANCE |
The wallet cannot cover the order. Nothing was charged and no order was created. |
| 403 | SCOPE_FORBIDDEN / ACCOUNT_BLOCKED / IP_NOT_ALLOWED |
The key does not hold this endpoint's scope, the account is blocked, or the request came from outside the key's IP allow-list. |
| 404 | NOT_FOUND / GAME_NOT_FOUND / ORDER_NOT_FOUND |
Nothing is served at that address, that product is not on sale, or that order is not yours (another account's id is a 404, never a 403). |
| 405 | METHOD_NOT_ALLOWED |
The HTTP method is not the one this endpoint accepts. |
| 409 | IDEMPOTENCY_CONFLICT / CONFLICT |
The Idempotency-Key was already used by this account for a different order. Reuse it only with the identical body; a key another account happens to use is that account's own and never collides with yours. |
| 422 | VALIDATION_FAILED / INVALID_REQUEST |
A field is missing, malformed or not something we sell. errors names each field. |
| 429 Retry-After: 60s | RATE_LIMITED |
Too many requests. Wait for the Retry-After header (seconds) before retrying. |
| 500 | INTERNAL_ERROR / SERVICE_TEMPORARILY_UNAVAILABLE |
An unexpected failure on our side. meta.request_id identifies it in our logs; retry with backoff. |
| 503 Retry-After: 60s | CATALOG_TEMPORARILY_UNAVAILABLE / SERVICE_TEMPORARILY_UNAVAILABLE |
The catalogue or a price could not be confirmed, or the service is temporarily unavailable. Nothing was charged; RETRY_AFTER seconds are enough before asking again. |
401 · No key
{
"success": false,
"error": {
"code": "MISSING_API_KEY",
"message": "X-API-Key header is required."
},
"error_code": "MISSING_API_KEY",
"message": "X-API-Key header is required.",
"meta": {
"request_id": "01J8ZQ4M6W9K2N7P3R5T8V0XYZ"
}
}403 · Missing scope
{
"success": false,
"error": {
"code": "SCOPE_FORBIDDEN",
"message": "This API key does not grant the \"catalog:read\" scope."
},
"error_code": "SCOPE_FORBIDDEN",
"message": "This API key does not grant the \"catalog:read\" scope.",
"meta": {
"request_id": "01J8ZQ4M6W9K2N7P3R5T8V0XYZ"
}
}422 · Validation failed
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "The request could not be validated."
},
"error_code": "VALIDATION_FAILED",
"message": "The request could not be validated.",
"errors": {
"game": [
"The game field is required."
]
},
"meta": {
"request_id": "01J8ZQ4M6W9K2N7P3R5T8V0XYZ"
}
}Order statuses
GET /orders/{orderId} publishes one of four statuses. delivery carries the redeem code only for a completed voucher order; it is null in every other state. These bodies are produced by the serializer the endpoint itself answers with.
created
{
"success": true,
"order_id": "WR-20261010-8F3KD",
"status": "processing",
"charged": "0.950000",
"currency": "USD",
"idempotent_replay": false,
"data": {
"order_id": "WR-20261010-8F3KD",
"status": "processing",
"charged": "0.950000",
"currency": "USD",
"idempotent_replay": false
},
"meta": {
"request_id": "01J8ZQ4M6W9K2N7P3R5T8V0XYZ"
}
}processing
{
"order_id": "WR-20261028-8F3KD",
"status": "processing",
"type": "game_topup",
"game": "pubgm",
"catalogue_name": "60 UC",
"player_id": "12345678",
"server_id": null,
"total_amount": "0.950000",
"currency": "USD",
"customer_reference": "MY-ORDER-123",
"delivery": null,
"created_at": "2026-10-10T09:00:00+00:00",
"updated_at": "2026-10-10T09:00:00+00:00"
}completed
{
"order_id": "WR-20261028-8F3KD",
"status": "completed",
"type": "game_topup",
"game": "pubgm",
"catalogue_name": "60 UC",
"player_id": "12345678",
"server_id": null,
"total_amount": "0.950000",
"currency": "USD",
"customer_reference": "MY-ORDER-123",
"delivery": {
"code": "WARYA-60UC-4KQ7T2M9"
},
"created_at": "2026-10-10T09:00:00+00:00",
"updated_at": "2026-10-10T09:00:00+00:00"
}failed
{
"order_id": "WR-20261028-8F3KD",
"status": "failed",
"type": "game_topup",
"game": "pubgm",
"catalogue_name": "60 UC",
"player_id": "12345678",
"server_id": null,
"total_amount": "0.950000",
"currency": "USD",
"customer_reference": "MY-ORDER-123",
"delivery": null,
"created_at": "2026-10-10T09:00:00+00:00",
"updated_at": "2026-10-10T09:00:00+00:00"
}refunded
{
"order_id": "WR-20261028-8F3KD",
"status": "refunded",
"type": "game_topup",
"game": "pubgm",
"catalogue_name": "60 UC",
"player_id": "12345678",
"server_id": null,
"total_amount": "0.950000",
"currency": "USD",
"customer_reference": "MY-ORDER-123",
"delivery": null,
"created_at": "2026-10-10T09:00:00+00:00",
"updated_at": "2026-10-10T09:00:00+00:00"
}Customer webhooks
Create an order with a callback_url and Warya pushes each order event to it. Each delivery is signed. The endpoint must be HTTPS; redirects are not followed and the host is re-resolved and re-checked at every attempt.
Events
order.createdorder.processingorder.completedorder.failedorder.refunded
Delivery
- Signature:
X-Warya-Signature - Timestamp:
X-Warya-Timestamp - Event id:
X-Warya-Event-Id - api_version:
2026-10-28 - Retries: 5 attempts, waits 1, 5, 15, 60 min
- Grace after rotation: 60 min
Verifying the signature
v1 is HMAC-SHA256 over "{t}.{event_id}.{raw_body}" keyed with your webhook secret. Recompute over the exact bytes you received and compare in constant time.
:raw_body = the exact bytes you received
t, v1 = the two values in X-Warya-Signature
expected = hmac_sha256(key = WEBHOOK_SECRET, message = t + "." + event_id + "." + raw_body)
ok = hash_equals(expected, v1) # compare in constant timeOpenAPI 3.1 specification
The machine-readable contract, reachable without a key. Import it into Postman, Insomnia or Swagger, inspect the schemas, or generate a typed SDK. The same document this reference renders from is available at the address below.
https://api.waryashop.org/openapi.json
Machine answer, human answer. Contract read from the running API.