One base URL for every 4Reply platform: https://api.4reply.com.
Unified API (/v1/..., this section): Free Fire top-ups, Razer Gold PINs and every ShamCash service are
listed, bought and followed the same way, with the same JSON.
Native APIs (/freefire/..., /razer/..., /shamcash/..., the next sections): each platform's own API,
unchanged. If you already integrated a platform, only the host changes.
One key for every platform: sign in at developers.4reply.com with your
4reply account to get your unified API key (4r_...). It works on every platform linked to your 4reply account,
here and on the platforms directly, sent the way each platform expects (see Authentication).
A platform account that is not linked to 4reply keeps using its own key.
Conventions: JSON bodies (Content-Type: application/json on POST), money amounts as decimal strings
("1.12", "19217.61"), times in ISO 8601 UTC (2026-10-16T12:00:00Z).
Getting started
Pick a service type:GET /v1/services → products (buy) or payments (ShamCash payment check).
Pick a project:GET /v1/projects?service=products → freefire, razer, shamcash.
List its products, with your own price:GET /v1/projects/{project}/products, with that platform's key.
Buy:POST /v1/projects/{project}/orders with your own order_id; the body is the same for every project.
Follow the order:GET /v1/projects/{project}/orders/{order_id}, or receive a webhook when it is final.
Products whose flow is inquiry_then_pay (ShamCash bills) take one more step before buying:
POST /v1/projects/shamcash/inquiries returns the amount due and an inquiry_id that the order then sends.
cURL
# products, with your Razer key
curl https://api.4reply.com/v1/projects/razer/products -H "X-API-Key: YOUR_RAZER_KEY"
# buy (your own order_id, UUID recommended)
curl -X POST https://api.4reply.com/v1/projects/razer/orders -H "X-API-Key: YOUR_RAZER_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id": "7d1c2a3e-4b5f-4a6b-9c8d-0e1f2a3b4c5d", "product_id": "freefire-pins:11572", "quantity": 1}'
# follow it
curl https://api.4reply.com/v1/projects/razer/orders/7d1c2a3e-4b5f-4a6b-9c8d-0e1f2a3b4c5d -H "X-API-Key: YOUR_RAZER_KEY"
Authentication
Every endpoint under /v1/projects/{project}/... takes your unified key (4r_..., from
developers.4reply.com) or, for an account not linked to 4reply, that platform's
own API key, in the header the platform expects:
Project
Header
freefire
Authorization: Bearer <key> or X-API-Key: <key> (unified 4r_..., or the reseller keyId.secret)
razer
X-API-Key: <key> (unified 4r_..., or the Razer key)
shamcash
X-API-Key: <key> (unified 4r_..., or sham_...)
Linking a platform account to 4reply retires that platform's old keys at once: use the unified key from then on.
IP allowlists, webhooks and API access are set per platform on developers.4reply.com.
/v1/services, /v1/projects, /v1/status and /health need no key.
The platform checks the key on every call; its own answer is passed on (401 PLATFORM_KEY_REJECTED,
403 PLATFORM_FORBIDDEN with the platform's message, e.g. an IP not in your allowlist).
Your server's IP is forwarded to the platform (X-Forwarded-For), so your platform IP allowlists keep working.
4Reply never stores your key. The only exception: an order with a webhook_url keeps it encrypted until its
webhook is delivered (the webhook carries it, see Webhooks), then deletes it. Orders are
filed under a fingerprint of the key (HMAC-SHA256), so only the same key can read them back.
Services
cURL
curl https://api.4reply.com/v1/services
JSON
{"data": [
{"id": "products", "name": "Products", "name_ar": "خدمات المنتجات",
"description": "Buy a product with the unified API: Free Fire top-ups, Razer Gold PINs and every ShamCash service (transfers, cash-in, mobile top-ups, MTN bundles, bills).",
"projects": ["freefire", "razer", "shamcash"],
"endpoints": [{"method": "GET", "path": "/v1/projects/{project}/products", "summary": "Products, with your price"},
{"method": "POST", "path": "/v1/projects/{project}/orders", "summary": "Buy"},
{"method": "GET", "path": "/v1/projects/{project}/orders/{order_id}", "summary": "Order status"}]},
{"id": "payments", "name": "Payments", "name_ar": "خدمات المدفوعات",
"description": "ShamCash payment-check API (API فحص الحوالات التلقائي): confirm the transfers you receive with your ShamCash transaction history and search. Called with your ShamCash key, exactly as on shamcash.4reply.com.",
"projects": ["shamcash"],
"endpoints": [{"method": "GET", "path": "/shamcash/account/history", "summary": "Last 10 operations of the account"},
{"method": "POST", "path": "/shamcash/account/history/search", "summary": "Find one operation by its tranId"},
{"method": "POST", "path": "/shamcash/account/history/filter", "summary": "Filter operations by tranId, peer account, currency or date range"}]}
]}
Service
Arabic
Projects
What it is
products
خدمات المنتجات
freefire, razer, shamcash
Everything you buy: Free Fire diamonds, Razer PINs and direct top-ups, and every ShamCash service: transfers, Syriatel / MTN Cash cash-in, top-ups, MTN bundles, postpaid and utility bills.
payments
خدمات المدفوعات
shamcash
ShamCash's payment-check API ("API فحص الحوالات التلقائي"): read your ShamCash history and search an operation to confirm a transfer you received. It is ShamCash's own API, used through /shamcash/account/history* (see the ShamCash API section, "Operation queries").
Projects
GET /v1/projects (optionally ?service=products|payments) and GET /v1/projects/{project}:
JSON
{"id": "razer", "name": "Razer Gold", "description": "Razer Gold PINs and direct top-ups bought from your own Razer accounts.",
"services": ["products"], "auth_header": "X-API-Key: <key>", "native_base_url": "https://api.4reply.com/razer",
"website": "https://rz.4reply.com"}
Free Fire: wallet (USD). Razer: site_wallet (USD, the site balance that pays commissions; the Razer Gold itself
is on your Razer accounts). ShamCash: one entry per currency.
Products
GET/v1/projects/{project}/products
Every product of the project in one shape, with your own price. Filters: ?category=, ?available=true,
?service=products|payments. Razer only: ?account_email= picks which of your Razer accounts' catalog (region)
and prices you see (default: your first active account).
Catalogs are cached about 60 seconds (Razer: about 10 minutes, since each Razer category is a live call to Razer;
use ?category= to load just one).
GET /v1/projects/{project}/products/{product_id} returns one product:
Product id inside its project; send it as product_id.
service
products (every product of every project).
category
The platform's own category (freefire-pins, mtn-bundles, transfer...).
name, name_ar
English name; Arabic name when the platform has one.
price
Your own unit price (your platform price rules applied: Free Fire customer price, Razer unitPrice + your commission, ShamCash charged price with tax), or null when there is no fixed price (custom amounts, bills) or it cannot be determined. A provider / base price is never shown instead.
Limits of amount for custom_amount products (amount must be a multiple of step).
cost_formula
When the platform charges more than amount, e.g. "ceil(amount * 1.04)" (Syriatel top-up).
fields
What to send in the order's fields. min/max are lengths for text and values for numbers; pattern is a regular expression; options lists the allowed values.
flow
direct, or inquiry_then_pay (inquiry first).
max_quantity
Highest quantity per order (1 for custom amounts and bills).
available
false when out of stock or disabled.
Buying
POST/v1/projects/{project}/orders
The same body for every project:
Field
Type
Meaning
order_id
string
Required. Your own unique id for the order (1-100 characters: letters, digits, ._:-). A UUID is recommended, and required for Free Fire (same rule as Free Fire's order_id). UUIDs are stored in lower case.
product_id
string
From the catalog.
quantity
integer
Default 1, at most the product's max_quantity.
fields
object
What the product's fields declare (player id, phone number, ShamCash address, Razer account...).
amount
string / number / null
Only for custom_amount products (optional for bills, where it must equal the bill).
inquiry_id
string
For inquiry_then_pay products.
bill_index
integer
Which bill of the inquiry to pay (default 0).
webhook_url
string
Optional https:// URL that receives the final result (see Webhooks).
Response 201 (a new order): the order object (below).
Your order_id is never executed twice, on any platform:
Sending the same order_id again with the same body returns the existing order (200) without buying again.
The same order_id with a different body is refused: 409 DUPLICATE_ORDER.
Free Fire: it is sent as Free Fire's own order_id (its native idempotency). Razer: it is sent as Razer's own
order_id on every purchase, and Razer's purchase log is looked up by it. ShamCash has no order id of its own:
4Reply records your order_id with ShamCash's transactionId.
A purchase can take 1-2 minutes on the platform. The request waits for the platform's answer (up to about
90 seconds) and returns the order; an order that takes longer (a slow checkout, several Razer PINs) is returned as
processing or pending and goes on in the background: read it again by its order_id, or use a webhook_url. A platform's refusal (insufficient balance, wrong phone number...) is a recorded order with
status: "failed" and an error; use a neworder_id to try again.
Inquiries
POST /v1/projects/{project}/inquiries with product_id, fields (and quantity). Required before ordering an
inquiry_then_pay product (ShamCash bills); for Free Fire it is an optional player check.
An inquiry pays one order, within 15 minutes. With several bills (landline), pick one with bill_index in the
order. GET /v1/projects/{project}/inquiries/{inquiry_id} reads it again.
Order status
GET/v1/projects/{project}/orders/{order_id}
The order, by your order_id, re-read from the platform when it is not final (at most every 20 seconds).
GET /v1/projects/{project}/orders lists your orders (newest first; status, limit up to 200, offset).
per PIN: SUCCESS → delivered; OUT_OF_STOCK, FAILED... → failed; details still loading → processing. The order is completed when every unit is delivered, failed when none is, completed + partial otherwise.
A purchase is never sent twice. If the platform does not answer (timeout, 5xx), the order is kept pending
with status_detail: "unconfirmed". Keep polling: Free Fire orders resolve from Free Fire's own order, Razer
orders from Razer's purchase log for your order_id. ShamCash has no order-status endpoint, so an unconfirmed
ShamCash order stays pending: check it in your ShamCash history (/shamcash/account/history/search) before
paying again with a neworder_id.
Razer orders of several units are bought one PIN at a time (Razer's order_id purchases are per unit), in
order, and stop at the first unit that is not delivered; the remaining units are reported failed with
NOT_ATTEMPTED and are never sent.
transaction_id, message, bill (the paid bill, for bill payments)
amount is the order total: price × quantity for fixed products (only units not failed, once known), the
amount you sent for custom amounts, the bill amount for bills.
Webhooks
Add webhook_url (https only) to an order and 4Reply POSTs the result once the order is final. The payload
is modelled on Free Fire's reseller webhook, so a Free Fire integration recognises it:
HTTP
POST https://shop.example.com/hooks/4reply
Content-Type: application/json
api-token: YOUR_RAZER_KEY
X-4Reply-Event: order.completed
User-Agent: 4reply-webhook/1.0
Rejected because of the input: invalid player id, unknown Razer account, wrong phone number or ShamCash account...
0
order.failed
Any other result: partial delivery, refund, other failure (message says why).
api-token is the same platform key the order was placed with, exactly as Free Fire sends it: Free Fire
<keyId>.<secret> (without Bearer), Razer and ShamCash the key itself. Compare it in constant time.
Answer with any 2xx quickly. Network errors, 429 and 5xx are retried after 30 s, 2 min, 10 min, 30 min and
2 h; other answers stop the deliveries. Key your handler by orderId: a retry may deliver twice.
Orders that are not final yet are followed in the background (reads only) for up to 24 hours. Your key is kept
encrypted only until the webhook is delivered (or given up), then deleted.
GET /v1/projects/{project}/orders/{order_id} stays the source of truth;
GET /v1/projects/{project}/orders/{order_id}/webhooks lists the delivery attempts.
Free Fire's own webhook (set in the Free Fire dashboard) keeps working unchanged and independently: it is
sent by Free Fire for every Free Fire order, with or without a unified webhook_url.
Errors
Every error of the unified API has the same envelope:
JSON
{"error": {"code": "MISSING_API_KEY", "message": "Send your razer API key: X-API-Key: <key>", "project": "razer", "upstream_status": null}}
project and upstream_status (the platform's HTTP status) are set when a platform is involved; some errors add
details. Branch on code, not on message. (The native sections return each platform's own errors.)
HTTP
code
Meaning
400
UPSTREAM_REJECTED
The platform refused the request (its message is passed on).
400
INQUIRY_FAILED
The bill / player look-up failed (e.g. "No bills found").
400
INQUIRY_NOT_SUPPORTED
The product needs no inquiry.
401
MISSING_API_KEY
No platform key sent (the message names the header).
401
PLATFORM_KEY_REJECTED
The platform does not accept the key.
402
UPSTREAM_SUBSCRIPTION_REQUIRED
The platform requires an active API subscription.
403
PLATFORM_FORBIDDEN
The platform refused the call (IP allowlist, no active session...).
This order_id exists with different parameters (details.order_id).
409
PRODUCT_UNAVAILABLE
Out of stock / disabled.
409
INQUIRY_USED
The inquiry already paid an order.
410
INQUIRY_EXPIRED
Make a new inquiry.
422
VALIDATION_ERROR
Malformed body or query.
422
INVALID_UUID_FORMAT
Free Fire order_id is not a UUID.
422
INVALID_FIELD
A product field is missing or wrong (details.field).
422
INVALID_AMOUNT, INVALID_QUANTITY, INVALID_BILL
amount, quantity or bill_index outside the product's limits.
422
INQUIRY_REQUIRED, INQUIRY_MISMATCH
Send a matching inquiry_id.
422
INVALID_WEBHOOK_URL
webhook_url must be a public https:// URL.
429
UPSTREAM_RATE_LIMITED
The platform's rate limit.
500
INTERNAL_ERROR
Gateway bug.
502
UPSTREAM_ERROR
The platform answered with an error.
503
UPSTREAM_UNAVAILABLE
The platform cannot be reached (nothing was sent).
504
UPSTREAM_TIMEOUT, UPSTREAM_NO_ANSWER
No answer in time (UPSTREAM_NO_ANSWER: a native write whose outcome is unknown).
A failed order carries the same kind of object in its error, e.g.
{"code": "INSUFFICIENT_BALANCE", "message": "Insufficient wallet balance", "upstream_status": 400}
(Free Fire codes such as PLAYER_NOT_FOUND or PRICE_INCREASED are passed on), PAYMENT_REJECTED (ShamCash),
NOT_ATTEMPTED (a Razer unit that was not sent), UPSTREAM_UNAVAILABLE (the request could not be sent; nothing
was charged) or ORDER_NOT_REGISTERED (Free Fire never received the order).
Each platform's own customer API is also reachable at https://api.4reply.com, byte for byte: same paths, same
headers, same bodies and answers. Every platform has the same base URL shape, https://api.4reply.com/<project>,
which stands for that platform's own API base URL:
Platform's own URL
Through 4Reply
https://ff.4reply.com/api/endpoints/v1/...
https://api.4reply.com/freefire/...
https://rz.4reply.com/api/...
https://api.4reply.com/razer/...
https://shamcash.4reply.com/api/...
https://api.4reply.com/shamcash/...
Only the customer API endpoints documented in the next sections are reachable; anything else answers 404.
Your server's IP is forwarded (X-Forwarded-For), so the platform's IP allowlist keeps working.
A request that is not a GET is sent once and never repeated; if no complete answer comes back the gateway
answers 504 UPSTREAM_NO_ANSWER (the outcome is unknown: check before sending it again).
GET /health answers {"ok": true, "version": "2.0.0"} without calling any platform.
Platform API
Free Fire API
Base URLhttps://api.4reply.com/freefire
A single REST interface to top up Free Fire: fetch catalog, validate player, create orders, and receive results via webhook or polling.
This is the direct server-to-server (H2H) top-up API. It is a standard JSON interface over HTTPS with Bearer authentication, UUID idempotency keys, published rate limits, and machine-readable error codes—enabling any store, checkout system, or bot to sell Free Fire diamonds programmatically without third-party SDKs.
This section reproduces the Free Fire reseller API documentation of ff.4reply.com. Through 4Reply the
requests and answers are exactly the same; only the host changes. The platform's own base URL keeps working.
Base URL
Base URL
Through 4Reply
https://api.4reply.com/freefire
Platform's own (still works)
https://ff.4reply.com/api/endpoints/v1
Every path documented here is relative to this base URL. Responses are standard JSON and always include a boolean success field, so a single check in your client separates success from all other outcomes.
Your first call, before writing code
Every endpoint is a standard HTTPS request with a single header. No SDK to install, no client library to import, no build steps. Paste the snippet below into your terminal to get a live catalog response.
HTTPS only: Non-secure connections are strictly rejected.
JSON format: All request payloads and response bodies use standard JSON.
Decimal string amounts: Currency amounts are always 6-decimal strings (e.g. "1.070000") to avoid floating-point inaccuracies.
UTC timestamps: All timestamps follow ISO 8601 in UTC (YYYY-MM-DDTHH:MM:SSZ).
Pagination: Listing endpoints return a pagination object with page, limit, total, and total_pages.
Idempotency: Order creation is safe against duplicates using the client-generated UUID in order_id.
Error Envelope
Failed responses maintain the same shape across all status codes. Always branch on error.code which is part of the API contract; never branch on error.message which is human-readable and subject to change.
Eleven endpoints covering the entire integration lifecycle: catalog discovery, real-time pricing, player validation, order creation, and post-order reconciliation.
Method
Path
Description
Rate Limit
GET
/account
Account details and wallet balance
60 req / 60s
GET
/catalog/big-categories
List big game categories
80 req / 60s
GET
/catalog/categories
List game categories (Free Fire)
80 req / 60s
GET
/catalog/subcategories
Free Fire diamond packages
80 req / 60s
GET
/catalog/subcategory/:itemId/price
Get real-time unit price
80 req / 60s
GET
/catalog/category/:categoryId/requirements
Required game fields (player_id)
80 req / 60s
POST
/player/validate
Validate player ID and in-game nickname
Dedicated limit
POST
/orders/create
Create direct top-up order with UUID
120 req / 60s
GET
/orders/:orderId
Query single order status by UUID
3 req / 60s
POST
/orders/batch
Batch order status query (up to 50)
2 req / 60s
GET
/orders
Paginated order history
2 req / 60s
Authentication & Access Control
Authentication requires a single HTTP header. Every request must include an Authorization header containing a Bearer credential composed of a Key ID and Secret separated by a dot:
HTTP
Authorization: Bearer <keyId>.<secret>
Key ID: Identifies your reseller account in the system.
Secret: Proves account ownership and is verified on the server using constant-time HMAC comparison.
No sessions or refresh tokens: No OAuth redirects, session cookies, or expiring tokens to schedule.
IP Allowlist: Pin API access to your server IPs. Requests from unauthorized IPs are rejected immediately with IP_NOT_ALLOWED (HTTP 403) before processing. Through 4Reply your server's IP is forwarded, so the allowlist keeps working.
Create a direct top-up order. The order_id field is your UUID idempotency key. Resending with the same UUID returns the existing record without duplicate charging.
Body Parameter
Type
Required
Description
Example
order_id
string (UUID)
yes
Unique order identifier in UUID v1/v4/v7 format
01912345-6789-7abc-8def-0123456789ab
sub_category_id
number
yes
Package item ID (e.g. 100)
100
quantity
number
yes
Number of units (positive integer >= 1)
1
requirements
object
yes
Fulfillment data (must contain player_id)
{"player_id": "14679257282"}
expected_unit_price
string
no
Price protection: rejected with PRICE_INCREASED if price rises
Register an HTTPS endpoint to receive final order results via POST. The callback carries the same API token as your reseller requests, in an api-token header.
This is Free Fire's own webhook, configured in the Free Fire dashboard. It keeps working unchanged and
independently of the unified API's webhook_url (see the Unified API section).
Status Code
Meaning & Trigger
status: 1 (accept)
Order completed successfully and diamonds were delivered to the player.
status: 2 (reject)
Invalid player ID only.
status: 0
Any other failure, including partial or refunded orders; message describes the reason.
webhook.test
Test notification triggered manually from the dashboard.
Header
Value
Content-Type
application/json
api-token
<keyId>.<secret> — same API token you use for reseller requests, without the Bearer prefix (or legacy ff_ key)
X-garena-Event
Event name, e.g. order.completed
User-Agent
garena-webhook/1.0
POST Payload - On Success (status: 1):
JSON
{
"orderId": "01912345-6789-7abc-8def-0123456789ab",
"message": "successfully charged 100+10 diamonds to player ProGamer",
"status": 1
}
Node.js Express - api-token Verification & Fast Ack:
Delivery & Retry Behavior. Deliveries are retried on network errors, HTTP 429 and 5xx, with a 10-second timeout per attempt. Respond with HTTP 2xx promptly. The GET /orders/:orderId endpoint remains the permanent source of truth. Return 200 immediately and process tasks asynchronously in your own queue.
Idempotent Handlers. Key your webhook handler by orderId to make duplicate deliveries safe. Crediting a customer twice is far worse than a delayed notification.
To configure your webhook endpoint, generate signing secrets, and send test events, go to Configure Webhooks in your Profile on ff.4reply.com.
Rate Limits & Error Handling
Limits are published, tracked per account, and enforced on a rolling window. Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
All error codes returned by the API, categorized with root causes and remediation steps:
Code
HTTP
Meaning
Remediation
MISSING_API_KEY
401
Missing Authorization header
Send header: Authorization: Bearer .
INVALID_API_KEY
401
API Key ID or Secret is invalid
Verify Key ID and Secret or regenerate them in Profile
ACCOUNT_DISABLED
403
Account disabled by administrator
Contact support to re-enable your account
ACCOUNT_FROZEN
403
Account is temporarily frozen
Contact support to unfreeze your account
IP_NOT_ALLOWED
403
Source IP address is not in allowlist
Add your server IP to the IP allowlist in Profile
MISSING_REQUIRED_FIELD
400
Required field missing in request body
Ensure player_id and order_id are provided
INVALID_UUID_FORMAT
400
order_id format is not a valid UUID
Use standard UUID format (v1, v4, v7)
INVALID_QUANTITY
400
Quantity must be an integer >= 1
Pass quantity >= 1
INVALID_CATEGORY_ID
400
Invalid category ID
Use categoryId=12 for Free Fire
INVALID_BIG_CATEGORY_ID
400
Invalid big category ID
Use bigCategoryId=1
INVALID_ITEM_ID
400
Invalid package item ID
Use valid item IDs from the catalog
INVALID_SUBCATEGORY
400
Requested diamond package does not exist
Check sub_category_id from catalog
INVALID_PRODUCT_CONFIG
400
Game requirements do not match
Check required fields in requirements object
INVALID_PAGINATION
400
page or limit values out of range
Pass page >= 1 and limit between 1 and 100
INVALID_DATE_FORMAT
400
Date format does not match ISO 8601
Use YYYY-MM-DDTHH:MM:SSZ format
PLAYER_NOT_FOUND
400
Player not found in Free Fire
Verify the player ID is correct
PLAYER_VALIDATION_FAILED
400
Game server connection failed during validation
Retry the request in a few moments
REGION_MISMATCH
400
Player region does not match expected region
Ensure server and region compatibility
INSUFFICIENT_BALANCE
400
Insufficient wallet balance for this order
Deposit funds to your wallet before placing orders
OUT_OF_STOCK
400
Product temporarily out of stock
Retry later or choose a different package
PRICE_INCREASED
400
Current price is higher than expected_unit_price
Read real-time price and update expected price
SUBCATEGORY_DISABLED
400
This product is temporarily disabled
Choose a different package
DUPLICATE_ORDER
409
Order with this UUID exists with different parameters
Generate a unique UUID for each new order
ORDER_NOT_FOUND
404
No order found with this UUID
Verify the queried UUID is correct
ITEM_NOT_FOUND
404
Catalog item not found
Query catalog to discover available IDs
ITEM_NOT_AVAILABLE
404
Item exists but is not currently purchasable
Retry later
INVALID_ORDER_IDS_FORMAT
400
order_ids field must be an array of strings
Pass order_ids as a string array
EMPTY_ORDER_IDS
400
order_ids array is empty
Provide at least one order ID
TOO_MANY_ORDER_IDS
400
Exceeded maximum limit (50 IDs)
Split request into batches of 50 or fewer
RATE_LIMIT_EXCEEDED
429
Rate limit exceeded for this endpoint
Wait the Retry-After seconds before retrying
INTERNAL_ERROR
500
Unexpected internal server error
Retry request or contact support
SERVICE_UNAVAILABLE
503
Service is under maintenance or overloaded
Wait a few minutes and retry
Integration FAQ
How does the reseller API authenticate requests?
With a single HTTP header: Authorization: Bearer <keyId>.<secret>. Key pairs are generated in your profile settings and verified on the server via HMAC on every request. No OAuth flows, cookies, or refresh tokens needed.
What is the base URL and API version my app should target?
The base URL is: https://api.4reply.com/freefire (or the platform's own https://ff.4reply.com/api/endpoints/v1). Point your application directly to it for all top-up and inquiry requests.
Which field carries the idempotency key in POST /orders/create?
The order_id field—a UUID you generate client-side and include in the creation body. If you resend with an existing order_id, the API returns the original order record without charging again.
My request timed out. Was the order created?
Query status rather than guessing: call GET /orders/:orderId with the same UUID you sent. If the order is returned, it was created and should not be duplicated. If you get ORDER_NOT_FOUND, nothing was charged and it is safe to retry.
How do I authenticate the webhook?
On your HTTPS endpoint, compare the Authorization header with the same Bearer <keyId>.<secret> token you use for API requests, using a constant-time comparison.
What should I do when I receive PRICE_INCREASED?
Fetch the latest price via GET /catalog/subcategory/:itemId/price, update expected_unit_price with the new value, and resubmit using the same order_id.
What happens when I exceed a rate limit?
You receive HTTP 429 with error code RATE_LIMIT_EXCEEDED and a Retry-After header. Wait the number of seconds specified in the header before retrying once.
What fields does POST /player/validate return?
It returns player_name and region. Call it to display the player nickname to your buyer before confirming the top-up and to prevent ID typos.
How do I map the requirements response into the order payload?
Each field_name returned by GET /catalog/category/:id/requirements should be passed as a key inside the requirements object in POST /orders/create (e.g. player_id for Free Fire).
What response is returned for requests from IPs outside the allowlist?
HTTP 403 with error code IP_NOT_ALLOWED before any business logic executes. This protects your account even if credentials leak.
How long does an order take to complete?
Treat completion as asynchronous; POST /orders/create returns immediately with status pending and final results arrive via webhook or subsequent polling.
Why must the API be called server-to-server only?
Because API keys carry direct financial debit permissions against your wallet balance. Never expose your Key ID or Secret in a client browser or mobile app.
Platform API
Razer API
Base URLhttps://api.4reply.com/razer
The Razer Gold store API, as documented on rz.4reply.com, reachable at https://api.4reply.com/razer.
Every request and answer is exactly the platform's own: only the host changes.
Base URL
Through 4Reply
https://api.4reply.com/razer
Platform's own (still works)
https://rz.4reply.com/api
So https://rz.4reply.com/api/get_products_categories is also https://api.4reply.com/razer/get_products_categories.
Authentication
Send your API key in the X-API-Key header of every request:
HTTP
X-API-Key: YOUR_RAZER_API_KEY
The API key, the API on/off switch and the API IP allowlist are on the API page of your Razer account (rz.4reply.com/api-access).
An active API subscription is required to send requests from outside the website, and API access must be turned on.
If you set an API IP allowlist, requests from other IP addresses are refused. Through 4Reply your server's IP is forwarded, so the allowlist keeps working.
A refused request answers with an HTTP error status and this body:
JSON
{
"success": false,
"error": {
"error_code": "SUBSCRIPTION_REQUIRED",
"message": "API access is not active for this account. Activate it from the API page of your account."
},
"error_code": "SUBSCRIPTION_REQUIRED",
"message": "API access is not active for this account. Activate it from the API page of your account."
}
Branch on the top-level error_code and show message; error is the original error (an object, or a plain string when the error has no code, and then the top-level error_code and message are absent).
Status
error_code
Meaning
401
(none)
Missing or wrong API key: {"success": false, "error": "Invalid or missing API key or session"}
402
SUBSCRIPTION_REQUIRED
No active API subscription for this account.
403
API_DISABLED
API access is turned off on the API page of your account.
403
IP_NOT_ALLOWED
The calling IP is not in the account's API IP allowlist (or the list is empty).
Add several accounts from one text. Each account is email, then password, then otp_secret; backup codes, recovery e-mails and any e-mail not followed by a password and an OTP secret are ignored.
Body (JSON):
Field
Type
Required
Description
raw_text
string
yes
Raw pasted accounts text
POST/get_accounts_balances
Get Accounts Balances
Gold and Silver balances of up to 500 accounts in one call. It can take tens of seconds.
Body (JSON):
Field
Type
Required
Description
emails
string[]
yes
Account emails to fetch
country_code
string (nullable)
no
Country code used for product-page proxy routing
include_silver
boolean
no
Whether to include silver balance payloads Default true.
allow_archived
boolean
no
Whether to allow fetching archived accounts Default false.
If true, return only pin codes in array format Default false.
country_code
string (nullable)
no
Country code used for product-page proxy routing
region_id
integer (nullable)
no
Razer catalog region id for checkout
payment_channel_id
integer (nullable)
no
Razer payment channel id for checkout
order_id
string (nullable)
no
Client-generated purchase order identifier
batch_id
string (nullable)
no
Client-generated batch identifier
batch_name
string (nullable)
no
Display name for the batch
batch_sequence
integer (nullable)
no
Batch sequence inside the order
batch_total_operations
integer (nullable)
no
Total planned operations for the batch
batch_operation_index
integer (nullable)
no
Current operation index inside the batch
POST/purchase_orders
Create Purchase Order
For large orders: saves the order plan (batches of products and quantities) without buying anything. Start it with POST /purchase_orders/{order_id}/execute.
order_id is yours to choose and must be unique; sending it again returns the same order. account_email also accepts auto to let the site pick the accounts.
Body (JSON):
Field
Type
Required
Description
order_id
string
yes
Unique client-generated order identifier
order_label
string (nullable)
no
Display label for the order
account_email
string
yes
Razer account email used by the order
country_code
string (nullable)
no
Country code used for the order
balance_before
number (nullable)
no
Balance before the order started
total_batches
integer
yes
Total batches inside the order
total_operations
integer
yes
Total planned operations inside the order
batches
PurchaseOrderBatchRequest[]
yes
Initial batch plan
excluded_accounts
string[] (nullable)
no
Emails of accounts excluded from auto purchases
account_quantities
object (nullable)
no
Per-account quantity caps in auto mode
pins_only
boolean
no
If true, return only pin codes in array format Default false.
Items of batches (PurchaseOrderBatchRequest):
Field
Type
Required
batch_id
string
yes
batch_name
string
yes
account_email
string (nullable)
no
assigned_account_email
string (nullable)
no
account_label
string (nullable)
no
account_allocations
object[] (nullable)
no
auto_account_selection
boolean (nullable)
no
source_batch_id
string (nullable)
no
source_batch_name
string (nullable)
no
product_id
integer
yes
product_category
string
yes
product_category_key
string (nullable)
no
player_id
string (nullable)
no
region_id
integer (nullable)
no
payment_channel_id
integer (nullable)
no
quantity
integer
yes
unit_price
number (nullable)
no
currency
string (nullable)
no
status
string (nullable)
no
processed
integer
no
successful
integer
no
failed
integer
no
pending
integer
no
cancelled
integer
no
balance_before
number (nullable)
no
balance_after
number (nullable)
no
POST/purchase_orders/{order_id}/execute
Execute Purchase Order
Start buying the order on the server. With run_in_background: true it answers at once; follow the progress with GET /purchase_orders/{order_id}.
Parameter
In
Type
Required
Default
order_id
path
string
yes
Body (JSON):
Field
Type
Required
Description
run_in_background
boolean
no
If true, schedule execution on the server and return immediately Default false.
order_label
string (nullable)
no
Optional order label used when creating the purchase order inline
account_email
string (nullable)
no
Account email used when creating the purchase order inline
country_code
string (nullable)
no
Country code used when creating the purchase order inline
balance_before
number (nullable)
no
Balance snapshot before starting the purchase order
total_batches
integer (nullable)
no
Expected number of batches in the purchase order
total_operations
integer (nullable)
no
Expected number of operations in the purchase order
batches
object[] (nullable)
no
Purchase batches to create and execute inline
excluded_accounts
string[] (nullable)
no
Emails of accounts excluded from auto purchases
account_quantities
object (nullable)
no
Per-account quantity caps in auto mode
pins_only
boolean
no
If true, return only pin codes in array format Default false.
GET/purchase_orders/{order_id}
Get Purchase Order
Order progress: successful, failed and remaining operations per batch.
Parameter
In
Type
Required
Default
order_id
path
string
yes
GET/purchase_orders/{order_id}/batch_operations
Get Purchase Batches Operations
The operations of the given batches with their codes. Pass comma-separated batch_ids.
Parameter
In
Type
Required
Default
order_id
path
string
yes
batch_ids
query
string
yes
limit_per_batch
query
integer
no
0
offset
query
integer
no
0
hydrate
query
boolean
no
true
refresh
query
boolean
no
false
POST/purchase_orders/{order_id}/stop
Stop Purchase Order
Stop the order at once; anything not bought yet is cancelled.
Parameter
In
Type
Required
Default
order_id
path
string
yes
Body (JSON):
Field
Type
Required
Description
stop_message
string (nullable)
no
Optional stop reason shown to the user
POST/get_transaction_information
Get Transaction Information
Get one transaction's information with transaction_number and email.
The user must send the e-mail and the transaction number.
The prices this customer pays: the site-wide prices with any the admin set for
them alone (customer_prices) in their place.
POST/purchase-fees/quote
Quote Purchase Fees
The commission each line of a cart will be charged.
Body (JSON):
Field
Type
Required
Description
items
FeeQuoteItem[]
no
Items of items (FeeQuoteItem):
Field
Type
Required
product_id
integer (nullable)
no
product_category
string (nullable)
no
unit_price
number (nullable)
no
quantity
integer
no
Account reload history
GET/account_reload_batches/{batch_id}
Get Account Reload Batch
Parameter
In
Type
Required
Default
batch_id
path
string
yes
Platform API
ShamCash API
Base URLhttps://api.4reply.com/shamcash
The ShamCash API, as documented on shamcash.4reply.com (translated to English), reachable at
https://api.4reply.com/shamcash. Requests and answers are exactly the platform's own: only the host changes.
Base URL
Through 4Reply
https://api.4reply.com/shamcash
Platform's own (still works)
https://shamcash.4reply.com/api
Every path below is relative to the base URL, e.g. POST https://api.4reply.com/shamcash/account/history/search.
Connection details
Send the key in the header of every request and do not share it.
API key
the X-API-Key header, e.g. X-API-Key: sham_xxxxxxxxxxxxxxxxx
Generating a new key stops the current key immediately; you will need to update it in your connected systems.
Each section needs one of the two paid API activations of your ShamCash account:
Payment-check API ("API فحص الحوالات التلقائي", the automatic transfer check API): the operation queries below.
Payments API: transfers, Syriatel, MTN, electronic payments and the additional services.
Store products work with either activation.
When the trial period has ended and the API is not activated, requests answer with the code
API_SUBSCRIPTION_REQUIRED. Through 4Reply your server's IP is forwarded, so an API IP allowlist keeps working.
Common error answers (result: 400, succeeded: false):
JSON
{"result": 400, "succeeded": false, "data": null, "message": "Insufficient balance to complete the transaction"}
JSON
{"result": 400, "succeeded": false, "data": null, "message": "Phone number is incorrect."}
JSON
{"result": 400, "succeeded": false, "data": null, "message": "Bill information is incorrect or cannot be paid right now."}
JSON
{"result": 400, "succeeded": false, "data": null, "message": "The service is stopped by the provider."}
Store products
Works with either API activation.
One link returns every sellable service as a separate product (money transfer per currency, Syriatel and MTN
top-up, bills and cash, every MTN bundle, and every electronic-payment biller), to add the products to any store
automatically instead of adding them by hand. Each product contains its fields (fields), its pricing (pricing)
and the request that executes it (order); products with flow = inquiry_then_pay need an inquiry (inquiry)
before paying. The transfer check service is not a product. Filter with ?category=transfer|mobile|electronic.
GET/store/products
All products: returns the categories and every product with how to execute it.
Placeholders in order.body / inquiry.body are written {fieldKey}; {amount} is the amount you charge and
{inquiry.x} a value returned by the inquiry. The paths in order.path are relative to the ShamCash site root and start with /api;
through 4Reply drop that /api: /api/mtn/bundles/activate is https://api.4reply.com/shamcash/mtn/bundles/activate.
GET/store/products/{product_id}
One product: returns one product by its id, such as mtn-bundle-3863, syriatel-units or biller-22.
Requires the payment-check API activation ("API فحص الحوالات التلقائي").
Requests that read the operation history and look up a specific operation, used to confirm the transfers you
receive. In the unified API this is the payments service type.
GET/account/history
Last 10 operations: fetches the last 10 operations of the account history.
{"ok": false, "message": "Transaction not found.", "data": {}, "result_code": 404}
POST/account/history/filter
Filter operations: filters the account's operations by operation number (tranId), the other party's account
address (peerAccount), the currency (currency or currencyId) or a date range (startDate and endDate,
format YYYY-MM-DD). At least one criterion is required, and several can be combined. Without dates the range
from 2000-01-01 to today is used, and every matching operation is returned at once. Leave out the fields you do
not fill in.
Field
Label
Required
tranId
Operation number
no
peerAccount
Other party's account address
no
currency
Currency: SYP (Syrian pound) or USD (dollar); empty for all currencies
Other answers: Phone Number Incorrect, Insufficient Balance, and Amount Out Of Range:
JSON
{"result": 400, "succeeded": false, "message": "Cash amount must be between 100 and 5500.", "data": null}
GET/mtn/bundles
MTN bundles: shows the available MTN bundles grouped by duration. paidAmount is the amount deducted (the price
with tax), and bundleId is what you send when activating.
Other answers: Unpayable Bill, Insufficient Balance.
Additional services
Requires the payments API activation.
General bills for any provider (serviceId), plus education and university services, electronic payment and
green energy. Each service's fields are set by the provider: use the services catalog to find the serviceId
values, then "billing fields" or the inquiry to find the required fields, then pay with the same values array.
GET/service/catalog
Services catalog: shows the available services and providers with each service's serviceId.
The store-products catalog points some products at these request paths (same platform endpoints, other
spellings); they are reachable the same way:
POST /syriatel/prepaid/recharge, POST /syriatel/postpaid/charge, POST /syriatel/cash-in,
POST /mtn/prepaid/recharge, POST /mtn/postpaid/charge, POST /mtn/cash-in,
POST /billing/landline/presentment, POST /billing/landline/pay, POST /green-energy/pay-by-type.