eGiftly Vendor API
Integrate digital gift card inventory, wallet balances, and order fulfillment directly into your platform. RESTful, JSON everywhere, and predictable errors.
https://vendor.e-giftly.com/api
https://sandbox.e-giftly.com/api
Authenticate
POST your credentials to /login and receive a bearer token.
Browse the catalog
List brands and denominations to find what to sell.
Create an order
Deduct wallet funds and receive voucher codes instantly.
# 1. Get an access token
curl -X POST https://vendor.e-giftly.com/api/login \
-H "Content-Type: application/json" \
-H "egiftly-client-id: YOUR_CLIENT_ID" \
-d '{"email":"vendor@example.com","password":"your_password"}'
# 2. Use the token to call any endpoint
curl https://vendor.e-giftly.com/api/wallet/balance \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "egiftly-client-id: YOUR_CLIENT_ID"
Welcome
The eGiftly API gives resellers a complete toolkit to manage digital gift card inventory, wallets, orders, and transactions — integrate gift card fulfillment into your own platform in an afternoon.
- User authentication and session management via JWT
- Real-time wallet balance and transaction tracking
- Access to a comprehensive brand and denomination catalog
- Order creation and management, with instant voucher delivery
- Webhook notifications for codes that resolve asynchronously
- RESTful architecture — predictable JSON in, JSON out
Authentication
The Vendor API uses JWT bearer authentication. Call Login to receive an access token, then include it in the Authorization header of every subsequent request. Every request must also include the egiftly-client-id header — provided to your account by eGiftly.
Required headers
| Header | Value |
|---|---|
| Authorization | Bearer {access_token} |
| egiftly-client-id | {your_client_id} |
Authentication errors
Failures at the authentication layer — a missing/invalid egiftly-client-id, or an expired, invalid, or blacklisted token — return a different envelope than regular endpoint errors, shown in the panel opposite.
Auth error codes
| Error ID | HTTP | Description |
|---|---|---|
| 2001 | 422 | egiftly-client-id is missing from the request header |
| 2002 | 401 | Not allowed to access vendor services (unknown client ID or invalid token user) |
| 2003 | 401 | Logged in user does not match passed vendor client id |
| 2004 | 401 | Logged in user does not match passed vendor client id |
| 2006 | 401 | Token is blacklisted, please login again |
| 2007 | 401 | Token has expired, please login again |
| 2008 | 401 | Token is invalid, please login again |
| 2009 | 401 | Something went wrong while getting the token, please login again |
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"token_type": "bearer",
"expires_in": 3600
}
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc..." \ -H "egiftly-client-id: YOUR_CLIENT_ID" \ https://vendor.e-giftly.com/api/wallet/balance
{
"error_id": 2007,
"error_message": "token has expired, please login again"
}
Error Handling
The API uses standard HTTP status codes to indicate success or failure. Error responses carry a descriptive message and, where applicable, an internal error code to help you branch on the exact failure.
Status codes
| Status | Meaning | Description |
|---|---|---|
| 200 | OK | Request successful, response contains data |
| 401 | Unauthorized | Missing or invalid authentication token |
| 404 | Not Found | Resource not found |
| 422 | Unprocessable Entity | Validation failed or business rule error — see the internal error code in the response body |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | Internal server error occurred |
Login
Authenticate a vendor account and receive a JWT access token. This token must be included in the Authorization header for all subsequent requests.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Required | Vendor account email address | |
| password | string | Required | Vendor account password |
Error codes
| Code | HTTP | Description |
|---|---|---|
| 0 | 200 | Successfully authenticated |
| 2002 | 422 | Email is missing from payload |
| 2003 | 422 | Password is missing from payload |
| 2004 | 422 | Invalid email format |
| 2006 | 401 | Invalid credentials or user not authorized |
curl -X POST https://vendor.e-giftly.com/api/login \
-H "Content-Type: application/json" \
-H "egiftly-client-id: YOUR_CLIENT_ID" \
-d '{
"email": "vendor@example.com",
"password": "your_password"
}'const response = await fetch('https://vendor.e-giftly.com/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'egiftly-client-id': 'YOUR_CLIENT_ID'
},
body: JSON.stringify({
email: 'vendor@example.com',
password: 'your_password'
})
});
const data = await response.json();
const token = data.data.access_token;
localStorage.setItem('token', token);$ch = curl_init('https://vendor.e-giftly.com/api/login');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'email' => 'vendor@example.com',
'password' => 'your_password'
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'egiftly-client-id: YOUR_CLIENT_ID'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
$token = $data['data']['access_token'];$response = Http::withHeaders([
'egiftly-client-id' => env('EGIFTLY_CLIENT_ID')
])->post('https://vendor.e-giftly.com/api/login', [
'email' => 'vendor@example.com',
'password' => 'your_password'
]);
$token = $response->json('data.access_token');{
"code": 0,
"message": "You have been logged in successfully",
"status": "success",
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "bearer",
"expires_in": 3600
}
}{
"code": 2002,
"message": "email or password are missing from your payload, please check your payload and try again",
"status": "error",
"data": []
}Wallet Balance
Retrieve the current wallet balance and status for the authenticated vendor.
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | Bearer token from login (Bearer {token}) |
| egiftly-client-id | string | Required | Client ID provided by eGiftly to your account |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Balance retrieved successfully |
| 401 | – | Unauthorized — invalid or expired token |
curl -X GET https://vendor.e-giftly.com/api/wallet/balance \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch('https://vendor.e-giftly.com/api/wallet/balance', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
});
const data = await response.json();
console.log(`Balance: ${data.data.balance}`);$ch = curl_init('https://vendor.e-giftly.com/api/wallet/balance');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'egiftly-client-id: ' . $clientId
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
$balance = $data['data']['balance'];$response = Http::withToken($token)
->withHeaders([
'egiftly-client-id' => env('EGIFTLY_CLIENT_ID')
])
->get('https://vendor.e-giftly.com/api/wallet/balance');
$balance = $response->json('data.balance');{
"code": 0,
"message": "Wallet details has been returned successfully",
"status": "success",
"data": {
"balance": 1500.50,
"status": "active"
}
}
Wallet Transactions
Paginated transaction history (20 items per page). Amounts are always positive — the transactionType field (fund or deduct) indicates the direction.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | integer | Optional | Page number (default: 1) |
| type | string | Optional | Filter by transaction type: fund or deduct |
| transactionId | string | Optional | Filter by transaction ID |
| transactionNumber | integer | Optional | Filter by transaction number |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Transactions retrieved successfully |
| 401 | – | Unauthorized — invalid token |
curl -X GET "https://vendor.e-giftly.com/api/wallet/transactions?page=1&type=deduct" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch(
'https://vendor.e-giftly.com/api/wallet/transactions',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();{
"code": 0,
"message": "Wallet transactions list has been returned successfully",
"status": "success",
"data": {
"data": [
{
"transactionNumber": 1001,
"transactionId": "0e8f2b9c-6a3d-4f1e-9c2a-7b5d8e4f1a23",
"transactionType": "deduct",
"TransactionAmount": "100.00",
"balanceBeforeTransaction": "1600.50",
"balanceAfterTransaction": "1500.50",
"transactionDate": "2026-03-18 14:20:00"
},
{
"transactionNumber": 1000,
"transactionId": "3f7a1c2d-8b4e-4a6f-b1c9-2e5d7f8a9b34",
"transactionType": "fund",
"TransactionAmount": "50.00",
"balanceBeforeTransaction": "1450.50",
"balanceAfterTransaction": "1500.50",
"transactionDate": "2026-03-17 10:15:00"
}
],
"current_page": 1,
"last_page": 5,
"per_page": 20,
"total": 100
}
}
Brands List
Get a paginated list of all available brands, including ID, name, and category.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| per_page | integer | Optional | Items per page (default: 20) |
| page | integer | Optional | Page number for pagination (default: 1) |
| brandName | string | Optional | Filter by brand name (partial match / wildcard search) |
| brandId | integer | Optional | Filter by brand ID |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Brands retrieved successfully |
| 401 | – | Unauthorized — invalid token |
curl -X GET "https://vendor.e-giftly.com/api/brands?per_page=20&page=1" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch('https://vendor.e-giftly.com/api/brands?per_page=20', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
});
const data = await response.json();
data.data.data.forEach(brand => {
console.log(`${brand.brand_name} - ${brand.category_name}`);
});$ch = curl_init('https://vendor.e-giftly.com/api/brands');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'egiftly-client-id: ' . $clientId
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
foreach ($data['data']['data'] as $brand) {
echo $brand['brand_name'] . "\n";
}$response = Http::withToken($token)
->withHeaders([
'egiftly-client-id' => env('EGIFTLY_CLIENT_ID')
])
->get('https://vendor.e-giftly.com/api/brands', [
'per_page' => 20,
'page' => 1
]);
$brands = $response->json('data.data');{
"code": 0,
"message": "",
"status": "success",
"data": {
"data": [
{ "brand_id": 1, "brand_name": "Amazon", "category_name": "Retail" },
{ "brand_id": 2, "brand_name": "iTunes", "category_name": "Digital" },
{ "brand_id": 3, "brand_name": "Playstation", "category_name": "Gaming" }
],
"current_page": 1,
"last_page": 8,
"per_page": 20,
"total": 150,
"next_page_url": "https://vendor.e-giftly.com/api/brands?page=2",
"prev_page_url": null
}
}
Denominations List
Get a paginated list of available denominations — values, pricing, and branding information.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| brandId | integer | Optional | Filter by brand ID |
| denominationId | integer | Optional | Filter by specific denomination ID |
| denominationName | string | Optional | Filter by denomination name (wildcard search) |
| per_page | integer | Optional | Items per page (default: 20) |
| page | integer | Optional | Page number for pagination (default: 1) |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Denominations retrieved successfully |
| 401 | – | Unauthorized — invalid token |
curl -X GET "https://vendor.e-giftly.com/api/denominations/list?brandId=1" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch(
'https://vendor.e-giftly.com/api/denominations/list?brandId=1',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
data.data.data.forEach(d => {
console.log(`${d.denominationName} - $${d.price}`);
});$ch = curl_init('https://vendor.e-giftly.com/api/denominations/list?brandId=1');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'egiftly-client-id: ' . $clientId
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
foreach ($data['data']['data'] as $denom) {
echo $denom['denominationName'] . "\n";
}$response = Http::withToken($token)
->withHeaders([
'egiftly-client-id' => env('EGIFTLY_CLIENT_ID')
])
->get('https://vendor.e-giftly.com/api/denominations/list', [
'brandId' => 1,
'per_page' => 20
]);
$denominations = $response->json('data.data');{
"code": 0,
"message": "Denominations list has been returned successfully",
"status": "success",
"data": {
"data": [
{
"brandId": 1,
"brandName": "Amazon",
"denominationId": 101,
"denominationName": "$10",
"price": 9.75,
"currency": "USD",
"egiftlyLogo": "https://cdn.e-giftly.com/logos/amazon-10.png",
"demominationUniqueRecordId": 501,
"suggestedPrice": 10.238
},
{
"brandId": 1,
"brandName": "Amazon",
"denominationId": 102,
"denominationName": "$25",
"price": 24.38,
"currency": "USD",
"egiftlyLogo": "https://cdn.e-giftly.com/logos/amazon-25.png",
"demominationUniqueRecordId": 502,
"suggestedPrice": 25.594
}
],
"current_page": 1,
"last_page": 10,
"per_page": 20,
"total": 200
}
}
Denomination Details
Get detailed information about a specific denomination, including pricing, suggested price, unique record ID, and live stock availability (available, inStock, canBePending).
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| denominationId | integer | Required | Denomination ID |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Denomination details retrieved |
| 404 | 0 | Denomination not found — note the body still carries code 0 / status success; rely on the HTTP status |
curl -X GET "https://vendor.e-giftly.com/api/denominations/101" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const denomId = 101;
const response = await fetch(
`https://vendor.e-giftly.com/api/denominations/${denomId}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
console.log(`Price: $${data.data.price}`);{
"code": 0,
"message": "Denomination details has been returned successfully",
"status": "success",
"data": {
"brandId": 1,
"denominationId": 101,
"denominationName": "$10",
"price": 9.75,
"egiftlyLogo": "https://cdn.e-giftly.com/logos/amazon-10.png",
"available": true,
"currency": "USD",
"uniqueDenominationId": 501,
"suggestedPrice": 10.238,
"inStock": true,
"canBePending": false
}
}{
"code": 0,
"message": "This denomination is not exist in our database, please check the denomination ID and try again",
"status": "success",
"data": []
}Orders List
Get a paginated list of all orders placed by the vendor, with status, amounts, and codes. orderStatus is one of success, canceled, pending, or refunded; orderType is Card Order or Direct Topup.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | integer | Optional | Page number (default: 1) |
| per_page | integer | Optional | Items per page (default: 20) |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Orders retrieved successfully |
| 401 | – | Unauthorized — invalid token |
curl -X GET "https://vendor.e-giftly.com/api/order/list?page=1&per_page=20" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch(
'https://vendor.e-giftly.com/api/order/list',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
console.log(`Total Orders: ${data.data.total}`);{
"code": 0,
"message": "Orders list has been returned successfully ",
"status": "success",
"data": {
"data": [
{
"orderId": 5432,
"orderStatus": "success",
"orderCreationDate": "2026-03-18 14:20:00",
"orderTransactionId": "0e8f2b9c-6a3d-4f1e-9c2a-7b5d8e4f1a23",
"orderAmount": "100.00",
"orderType": "Card Order",
"vendorOrderUniqueId": "CUST-ORD-001",
"codes": [
{
"brandName": "Amazon",
"brandId": 1,
"denominationName": "$50",
"code": "XXXX-XXXX-XXXX-XXXX",
"serialNumber": "SN123456789",
"expiryDate": "2027-03-18",
"created_at": "2026-03-18"
}
]
},
{
"orderId": 5431,
"orderStatus": "success",
"orderCreationDate": "2026-03-17 10:15:00",
"orderTransactionId": "3f7a1c2d-8b4e-4a6f-b1c9-2e5d7f8a9b34",
"orderAmount": "50.00",
"orderType": "Card Order",
"vendorOrderUniqueId": "CUST-ORD-002",
"codes": [
{
"brandName": "iTunes",
"brandId": 2,
"denominationName": "$25",
"code": "YYYY-YYYY-YYYY-YYYY",
"serialNumber": "SN987654321",
"expiryDate": "2027-03-17",
"created_at": "2026-03-17"
}
]
}
],
"current_page": 1,
"last_page": 5,
"per_page": 20,
"total": 100
}
}
Create Order
Create a new order for gift cards. Deducts funds from the vendor wallet and returns voucher codes immediately on success.
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| uniqueOrderId | string | Required | Unique order identifier from vendor system (max 255 characters) |
| denominationId | integer | Required | Denomination ID to order (minimum: 1) |
| quantity | integer | Optional | Number of cards to order (1–500, default: 1) |
Error codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Order created successfully |
| 422 | 3001 | uniqueOrderId is missing or invalid |
| 422 | 3003 | denominationId or quantity is missing or invalid |
| 404 | 3005 | Denomination does not exist in database |
| 422 | 3006 | Picked denomination is inactive or not found |
| 422 | 3007 | Insufficient wallet balance |
| 422 | 3008 | Issue while deducting from wallet |
| 422 | 3009 | Error while creating order |
| 404 | 3010 | Vendor wallet not found |
curl -X POST https://vendor.e-giftly.com/api/order/create \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "egiftly-client-id: YOUR_CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{
"uniqueOrderId": "CUST-ORD-001",
"denominationId": 101,
"quantity": 2
}'const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch('https://vendor.e-giftly.com/api/order/create', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
uniqueOrderId: 'CUST-ORD-001',
denominationId: 101,
quantity: 2
})
});
const data = await response.json();
if (data.status === 'success') {
console.log(`Order ID: ${data.data.orderId}`);
console.log(`Codes:`, data.data.code);
}$ch = curl_init('https://vendor.e-giftly.com/api/order/create');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'uniqueOrderId' => 'CUST-ORD-001',
'denominationId' => 101,
'quantity' => 2
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'egiftly-client-id: ' . $clientId,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
if ($data['status'] === 'success') {
$orderId = $data['data']['orderId'];
}$response = Http::withToken($token)
->withHeaders([
'egiftly-client-id' => env('EGIFTLY_CLIENT_ID')
])
->post('https://vendor.e-giftly.com/api/order/create', [
'uniqueOrderId' => 'CUST-ORD-001',
'denominationId' => 101,
'quantity' => 2
]);
if ($response->json('status') === 'success') {
$orderId = $response->json('data.orderId');
$codes = $response->json('data.code');
}{
"code": 0,
"message": "Order has been created successfully",
"status": "success",
"data": {
"orderId": 5432,
"transactionId": "0e8f2b9c-6a3d-4f1e-9c2a-7b5d8e4f1a23",
"orderAmount": 100.00,
"deductedAmount": 100.00,
"balanceAfterPurchase": 1400.50,
"requestedQuantity": 2,
"purchasedQuantity": 2,
"code": [
{
"brandName": "Amazon",
"denominationName": "$50",
"code": "XXXX-XXXX-XXXX-XXXX",
"serialNumber": "SN123456789",
"expiryDate": "2027-03-18",
"created_at": "2026-03-18T14:20:00.000000Z"
},
{
"brandName": "Amazon",
"denominationName": "$50",
"code": "YYYY-YYYY-YYYY-YYYY",
"serialNumber": "SN987654321",
"expiryDate": "2027-03-18",
"created_at": "2026-03-18T14:20:00.000000Z"
}
]
}
}{
"code": 3007,
"message": "Your wallet does not have enough balance to make this purchase",
"status": "error",
"data": []
}Order Codes
Retrieve voucher codes and delivery information for a completed order — all codes with expiry dates and serial numbers.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| orderId | integer | Required | Order ID to retrieve codes for |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Order codes retrieved |
| 500 | – | Order ID does not exist or does not belong to your account |
curl -X GET "https://vendor.e-giftly.com/api/order/codes/5432" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const orderId = 5432;
const response = await fetch(
`https://vendor.e-giftly.com/api/order/codes/${orderId}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
data.data.codes.forEach(code => {
console.log(`Code: ${code.code}`);
});{
"code": 0,
"message": "Order has been returned successfully ",
"status": "success",
"data": {
"orderId": 5432,
"orderStatus": "success",
"orderCreationDate": "2026-03-18 14:20:00",
"orderTransactionId": "0e8f2b9c-6a3d-4f1e-9c2a-7b5d8e4f1a23",
"orderAmount": "100.00",
"orderType": "Card Order",
"vendorOrderUniqueId": "CUST-ORD-001",
"codes": [
{
"brandName": "Amazon",
"brandId": 1,
"denominationId": 101,
"denominationName": "$50",
"denominationPaidPricePerItem": "48.75",
"code": "XXXX-XXXX-XXXX-XXXX",
"serialNumber": "SN123456789",
"expiryDate": "2027-03-18",
"created_at": "2026-03-18"
},
{
"brandName": "Amazon",
"brandId": 1,
"denominationId": 101,
"denominationName": "$50",
"denominationPaidPricePerItem": "48.75",
"code": "YYYY-YYYY-YYYY-YYYY",
"serialNumber": "SN987654321",
"expiryDate": "2027-03-18",
"created_at": "2026-03-18"
}
]
}
}
Topup Denominations
List the eGiftly direct top-up catalog available to your account — recharge products such as game credits (e.g. PUBG UC-style products) that are fulfilled directly against a player/customer account instead of issuing a redeemable code. Pricing already includes your account's markup; no supplier information is ever included in this response.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| brandId | integer | Optional | Filter by brand ID |
| per_page | integer | Optional | Items per page (default: 20) |
| page | integer | Optional | Page number for pagination (default: 1) |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Top-up denominations retrieved successfully |
| 401 | – | Unauthorized — invalid token |
curl -X GET "https://vendor.e-giftly.com/api/topups/denominations?brandId=12" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch(
'https://vendor.e-giftly.com/api/topups/denominations?brandId=12',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
data.data.items.forEach(item => {
console.log(`${item.brand.name} - ${item.name} - $${item.price}`);
});{
"code": 0,
"message": "Top-up denominations fetched successfully",
"status": "success",
"data": {
"items": [
{
"id": 301,
"brand": { "id": 12, "name": "PUBG Mobile" },
"name": "660 UC",
"price": 9.99,
"currency": "USD",
"available": true
},
{
"id": 302,
"brand": { "id": 12, "name": "PUBG Mobile" },
"name": "1800 UC",
"price": 24.5,
"currency": "USD",
"available": true
}
],
"pagination": {
"current_page": 1,
"per_page": 20,
"total": 45,
"total_pages": 3
}
}
}
Topup Config
Get the dynamic customer-input schema for a top-up denomination, so you can render the right form fields (player ID, server, zone, etc.) without hard-coding anything per product. The schema reflects the highest-priority active supplier mapping for this denomination — the same one that will actually fulfill a purchase made right now via Create Topup.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | integer | Required | Top-up denomination ID (from Topup Denominations) |
Response field schema
| Field | Type | Description |
|---|---|---|
| key | string | Parameter key to send back in parameters when creating the top-up |
| label | string | Human-readable label for the field |
| type | string | Input type, e.g. string or select |
| required | boolean | Always true — every returned field must be supplied |
| options | array|null | Choice list when type is select; null otherwise |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Configuration retrieved successfully |
| 404 | 6002 | This top-up denomination does not exist or is not available |
| 422 | 6006 | This top-up denomination is not currently fulfillable, please try again later |
curl -X GET "https://vendor.e-giftly.com/api/topups/denominations/301/config" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const denomId = 301;
const response = await fetch(
`https://vendor.e-giftly.com/api/topups/denominations/${denomId}/config`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
data.data.fields.forEach(f => console.log(f.key, f.label, f.type));{
"code": 0,
"message": "Top-up denomination configuration fetched successfully",
"status": "success",
"data": {
"denominationId": 301,
"fields": [
{ "key": "playerId", "label": "Player ID", "type": "string", "required": true, "options": null },
{ "key": "serverId", "label": "Server", "type": "select", "required": true, "options": ["Asia", "Europe", "NA"] }
]
}
}{
"code": 6002,
"message": "This top-up denomination does not exist or is not available",
"status": "error",
"data": []
}{
"code": 6006,
"message": "This top-up denomination is not currently fulfillable, please try again later",
"status": "error",
"data": []
}Create Topup
Submit a direct top-up for a customer's account or player ID. Deducts the priced amount from your wallet and forwards the purchase to the fulfilling supplier. Unlike Create Order, a top-up does not return a redeemable code — it resolves asynchronously against the customer's account, so the response reflects the order's state right after submission, not its final outcome. Poll Topup Status or listen for Topup Webhooks to know when it completes.
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| Idempotency-Key | string | Optional | Extra idempotency guard on top of customer_reference — replaying the same key returns the original order instead of creating or charging a new one |
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| denomination_id | integer | Required | Top-up denomination ID to purchase (minimum: 1) |
| customer_reference | string | Required | Your unique reference for this top-up (max 255 characters). Reusing the same reference for the same vendor replays the original order rather than charging again |
| parameters | object | Required | Key/value pairs matching the keys returned by Topup Config for this denomination (e.g. player ID, server) |
pending, processing, completed, failed, refunded, or unknown. unknown means the supplier request itself failed or timed out — the purchase may or may not have gone through on their end, so it is held for manual reconciliation rather than assumed successful or failed.Error codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Top-up request submitted (or idempotent replay of an already-processed request) |
| 422 | 6001 | Invalid request payload — see the message for the specific field |
| 404 | 6002 | This top-up denomination does not exist |
| 422 | 6003 | This top-up denomination is currently unavailable |
| 422 | 6004 | One or more parameters values failed validation against the field schema |
| 404 | 6005 | Vendor wallet not found |
| 422 | 6005 | Insufficient wallet balance |
| 422 | 6006 | No active supplier mapping currently available for this denomination |
| 422 | 6007 | Unable to charge your wallet for this purchase |
| 422 | 6011 | The provided account/player information could not be verified |
curl -X POST https://vendor.e-giftly.com/api/topups \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "egiftly-client-id: YOUR_CLIENT_ID" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1a3e2b-9c4d-4a5e-8b7c-1d2e3f4a5b6c" \
-d '{
"denomination_id": 301,
"customer_reference": "CUST-TOPUP-001",
"parameters": {
"playerId": "5123456789",
"serverId": "Asia"
}
}'const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch('https://vendor.e-giftly.com/api/topups', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID()
},
body: JSON.stringify({
denomination_id: 301,
customer_reference: 'CUST-TOPUP-001',
parameters: {
playerId: '5123456789',
serverId: 'Asia'
}
})
});
const data = await response.json();
if (data.status === 'success') {
console.log(`Topup ID: ${data.data.id}, status: ${data.data.status}`);
}$ch = curl_init('https://vendor.e-giftly.com/api/topups');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'denomination_id' => 301,
'customer_reference' => 'CUST-TOPUP-001',
'parameters' => [
'playerId' => '5123456789',
'serverId' => 'Asia'
]
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'egiftly-client-id: ' . $clientId,
'Content-Type: application/json',
'Idempotency-Key: ' . \Illuminate\Support\Str::uuid()
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
if ($data['status'] === 'success') {
$topupId = $data['data']['id'];
}$response = Http::withToken($token)
->withHeaders([
'egiftly-client-id' => env('EGIFTLY_CLIENT_ID'),
'Idempotency-Key' => (string) Str::uuid()
])
->post('https://vendor.e-giftly.com/api/topups', [
'denomination_id' => 301,
'customer_reference' => 'CUST-TOPUP-001',
'parameters' => [
'playerId' => '5123456789',
'serverId' => 'Asia'
]
]);
if ($response->json('status') === 'success') {
$topupId = $response->json('data.id');
$status = $response->json('data.status');
}{
"code": 0,
"message": "Top-up request has been submitted",
"status": "success",
"data": {
"id": 9001,
"customerReference": "CUST-TOPUP-001",
"denominationId": 301,
"status": "processing",
"price": 10.49,
"currency": "USD",
"createdAt": "2026-08-20T14:20:00.000000Z",
"completedAt": null
}
}{
"code": 6005,
"message": "Your wallet does not have enough balance to make this purchase",
"status": "error",
"data": []
}Topup Status
Poll the current status of a top-up order — useful while it is still pending, processing, or unknown. Prefer Topup Webhooks for real-time updates and use this endpoint to reconcile or for a manual check.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | integer | Required | Top-up order ID, as returned in id from Create Topup or Topup Webhooks |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Top-up order status retrieved |
| 404 | 6010 | This top-up order does not exist, or does not belong to your account |
curl -X GET "https://vendor.e-giftly.com/api/topups/9001/status" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const topupId = 9001;
const response = await fetch(
`https://vendor.e-giftly.com/api/topups/${topupId}/status`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
console.log(`Status: ${data.data.status}`);{
"code": 0,
"message": "Top-up order status fetched successfully",
"status": "success",
"data": {
"id": 9001,
"customerReference": "CUST-TOPUP-001",
"denominationId": 301,
"status": "completed",
"price": 10.49,
"currency": "USD",
"createdAt": "2026-08-20T14:20:00.000000Z",
"completedAt": "2026-08-20T14:20:42.000000Z"
}
}{
"code": 6010,
"message": "This top-up order does not exist",
"status": "error",
"data": []
}Range Denominations
List the eGiftly range / open-amount catalog available to your account — products such as Binance USDT vouchers or open-value gift cards where the customer chooses a custom face value within a min/max range, instead of picking a fixed SKU. Pricing already includes your account's markup; no supplier information is ever included in this response. If the same product is offered by more than one supplier, only the highest-priority active mapping is shown — you never see duplicate entries for one product.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| brandId | integer | Optional | Filter by brand ID |
RD-1MUZNII9) — never the internal denomination id, and never a sequential number you can enumerate. Save it as-is; it stays valid even if the product's backing supplier changes later. Use it as-is in Create Range Order.Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Range denominations fetched successfully |
| 401 | – | Unauthorized — invalid token |
curl -X GET "https://vendor.e-giftly.com/api/range-denominations/list?brandId=103" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch(
'https://vendor.e-giftly.com/api/range-denominations/list?brandId=103',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
data.data.forEach(item => {
console.log(`${item.name} - ${item.minAmount}-${item.maxAmount} ${item.currency} @ ${item.unitPrice}/unit`);
});{
"code": 0,
"message": "Range denominations fetched successfully",
"status": "success",
"data": [
{
"code": "RD-1MUZNII9",
"name": "Binance 1 - 500 USDT Open Amount",
"brandId": 103,
"brandName": "Binance",
"currency": "USD",
"minAmount": 1.1,
"maxAmount": 500,
"unitPrice": 1.04
}
]
}
Range Denomination Details
Fetch a single range denomination by its opaque code — the same shape as one item from Range Denominations. Useful to re-check current pricing and min/max before submitting Create Range Order, since both are resolved fresh against the current priority supplier mapping every time.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| code | string | Required | Range denomination code, as returned in code from Range Denominations |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Range denomination fetched successfully |
| 404 | 6002 | This range denomination does not exist |
curl -X GET "https://vendor.e-giftly.com/api/range-denominations/RD-1MUZNII9" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const code = 'RD-1MUZNII9';
const response = await fetch(
`https://vendor.e-giftly.com/api/range-denominations/${code}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
console.log(`${data.data.minAmount}-${data.data.maxAmount} ${data.data.currency}`);{
"code": 0,
"message": "Range denomination fetched successfully",
"status": "success",
"data": {
"code": "RD-1MUZNII9",
"name": "Binance 1 - 500 USDT Open Amount",
"brandId": 103,
"brandName": "Binance",
"currency": "USD",
"minAmount": 1.1,
"maxAmount": 500,
"unitPrice": 1.04
}
}{
"code": 6002,
"message": "This range denomination does not exist",
"status": "error",
"data": []
}Create Range Order
Submit a range/open-amount order for a customer-chosen face value. Deducts the priced amount from your wallet and forwards the purchase to the fulfilling supplier. Unlike a top-up, a range order does produce a real, redeemable code — but like a top-up, fulfillment happens asynchronously (a live supplier call can take a few seconds), so the response reflects the order's state right after submission, not its final outcome. Poll Range Order Status or listen for Range Webhooks to get the code once it's ready.
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| uniqueOrderId | string | Required | Your own idempotency key (max 255 characters). Retrying the same value returns the original order instead of charging again |
| code | string | Required | Range denomination code, from Range Denominations |
| amount | number | Required | Requested face value, in the denomination's own currency. Must fall within minAmount/maxAmount for the product (minimum: 0.01) |
processing, completed, failed, or refunded. orderId is an opaque identifier (e.g. RG-20260909162409HU56) — save it as-is to poll Range Order Status.Error codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Range order submitted (or idempotent replay of an already-processed request) |
| 422 | 6001 | Invalid request payload — see the message for the specific field |
| 404 | 6002 | This range denomination does not exist |
| 422 | 6004 | amount is outside the product's min/max range — see the message for the exact bound |
| 404 | 6005 | Vendor wallet not found |
| 422 | 6005 | Insufficient wallet balance |
| 422 | 6007 | Unable to charge your wallet for this purchase |
| 422 | 6008 | There was an issue creating your order — the charge, if any, was automatically refunded |
curl -X POST https://vendor.e-giftly.com/api/range-orders/create \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "egiftly-client-id: YOUR_CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{
"uniqueOrderId": "CUST-RANGE-001",
"code": "RD-1MUZNII9",
"amount": 10
}'const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const response = await fetch('https://vendor.e-giftly.com/api/range-orders/create', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
uniqueOrderId: 'CUST-RANGE-001',
code: 'RD-1MUZNII9',
amount: 10
})
});
const data = await response.json();
if (data.status === 'success') {
console.log(`Order ID: ${data.data.orderId}, status: ${data.data.status}`);
}$ch = curl_init('https://vendor.e-giftly.com/api/range-orders/create');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'uniqueOrderId' => 'CUST-RANGE-001',
'code' => 'RD-1MUZNII9',
'amount' => 10
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'egiftly-client-id: ' . $clientId,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
if ($data['status'] === 'success') {
$orderId = $data['data']['orderId'];
}$response = Http::withToken($token)
->withHeaders(['egiftly-client-id' => env('EGIFTLY_CLIENT_ID')])
->post('https://vendor.e-giftly.com/api/range-orders/create', [
'uniqueOrderId' => 'CUST-RANGE-001',
'code' => 'RD-1MUZNII9',
'amount' => 10
]);
if ($response->json('status') === 'success') {
$orderId = $response->json('data.orderId');
$status = $response->json('data.status');
}{
"code": 0,
"message": "Range order has been submitted",
"status": "success",
"data": {
"orderId": "RG-20260909162409HU56",
"uniqueOrderId": "CUST-RANGE-001",
"status": "processing",
"requestedAmount": 10,
"price": 10.40,
"currency": "USD",
"createdAt": "2026-09-09T16:24:09.000000Z",
"code": null
}
}{
"code": 6004,
"message": "Amount must be at most 500.0000",
"status": "error",
"data": []
}Range Order Status
Poll the current status of a range order — useful while it is still processing. Prefer Range Webhooks for real-time delivery of the code and use this endpoint to reconcile or for a manual check.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| orderNumber | string | Required | Opaque order ID, as returned in orderId from Create Range Order or Range Webhooks |
Status codes
| HTTP | Code | Description |
|---|---|---|
| 200 | 0 | Order status fetched successfully |
| 404 | 6010 | This order does not exist, or does not belong to your account |
curl -X GET "https://vendor.e-giftly.com/api/range-orders/RG-20260909162409HU56/status" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "egiftly-client-id: YOUR_CLIENT_ID"
const token = localStorage.getItem('token');
const clientId = 'YOUR_CLIENT_ID';
const orderId = 'RG-20260909162409HU56';
const response = await fetch(
`https://vendor.e-giftly.com/api/range-orders/${orderId}/status`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'egiftly-client-id': clientId
}
}
);
const data = await response.json();
console.log(`Status: ${data.data.status}`);{
"code": 0,
"message": "Order status fetched successfully",
"status": "success",
"data": {
"orderId": "RG-20260909162409HU56",
"uniqueOrderId": "CUST-RANGE-001",
"status": "completed",
"requestedAmount": 10,
"price": 10.40,
"currency": "USD",
"createdAt": "2026-09-09T16:24:09.000000Z",
"code": {
"code": "BMD49AHC7WX8EV66",
"serialNumber": "",
"expiryDate": null
}
}
}{
"code": 0,
"message": "Order status fetched successfully",
"status": "success",
"data": {
"orderId": "RG-20260909162409HU56",
"uniqueOrderId": "CUST-RANGE-001",
"status": "processing",
"requestedAmount": 10,
"price": 10.40,
"currency": "USD",
"createdAt": "2026-09-09T16:24:09.000000Z",
"code": null
}
}{
"code": 6010,
"message": "This order does not exist",
"status": "error",
"data": []
}Code Webhooks
Some suppliers don't deliver a code instantly — the order goes pending and the code is fetched later by a background process. When that happens, eGiftly notifies you by sending a webhook to the webhook URL configured on your account (contact your account manager to set or change it). One webhook is sent per code as each pending code resolves. If your account has no webhook URL configured, no webhooks are sent — keep polling GET /api/order/codes/{orderId} instead.
Delivery
| Method | POST with a JSON body |
| Headers | client-id: {your_vendor_client_id}, Content-Type: application/json |
| Expected response | Respond with HTTP 200 to acknowledge receipt |
| Retries | Non-200 (or failed) deliveries are retried every 5 minutes, up to 5 attempts, with the identical payload |
| Idempotency | Deduplicate on eventReference — retries reuse the same value |
Common fields
| Field | Type | Description |
|---|---|---|
| event | string | order.code.ready or order.code.refunded |
| eventReference | integer | Unique reference for this event — use it to deduplicate retries |
| orderId | integer | eGiftly order ID (same as Orders List / Order Codes) |
| vendorOrderUniqueId | string | The uniqueOrderId you sent when creating the order |
| pendingCodesRemaining | integer | Codes of this order still pending after this event — 0 means fully resolved |
| date | string | Event timestamp (ISO 8601) |
| signature | string | Signature of the payload — see verification below |
How the signature is generated
eGiftly builds the signature from the payload itself using the vendor_public_key and vendor_secret_key provided to your account:
- Take the payload without the
signaturefield, keys in the exact order shown in the payload examples. - Serialize to JSON using PHP
json_encode()defaults — no spaces, forward slashes escaped as\/. - Encrypt with AES-256-CBC —
vendor_public_keyas the encryption key,vendor_secret_keyas the IV (first 16 bytes if longer). - Base64-encode the output a second time. The result is the
signaturevalue.
JSON.stringify() doesn't escape slashes). Prefer the decrypt approach: decrypt the signature with AES-256-CBC, parse the resulting JSON, and compare field values against the received payload instead of comparing raw strings.{
"event": "order.code.ready",
"eventReference": 9812,
"orderId": 5432,
"vendorOrderUniqueId": "CUST-ORD-001",
"code": {
"brandName": "Amazon",
"brandId": 1,
"denominationId": 101,
"denominationName": "$50",
"code": "XXXX-XXXX-XXXX-XXXX",
"serialNumber": "SN123456789",
"expiryDate": null,
"created_at": "2026-07-23T14:20:00.000000Z"
},
"pendingCodesRemaining": 0,
"date": "2026-07-23T14:20:00.000000Z",
"signature": "b64EncodedSignatureString=="
}{
"event": "order.code.refunded",
"eventReference": 9813,
"orderId": 5432,
"vendorOrderUniqueId": "CUST-ORD-001",
"refund": {
"brandName": "Amazon",
"brandId": 1,
"denominationId": 101,
"denominationName": "$50",
"refundedAmount": 48.75,
"orderFullyRefunded": false
},
"pendingCodesRemaining": 0,
"date": "2026-07-23T14:20:00.000000Z",
"signature": "b64EncodedSignatureString=="
}// $payload = webhook body WITHOUT "signature", keys in received order
$signature = base64_encode(
openssl_encrypt(
json_encode($payload),
'AES-256-CBC',
$yourPublicKey, // provided by eGiftly (encryption key)
0, // options = 0, openssl returns base64 output
$yourSecretKey // provided by eGiftly (used as the IV)
)
);
$payload = json_decode($request->getContent(), true);
$receivedSignature = $payload['signature'];
unset($payload['signature']);
// rebuild the signature from the payload you received
$expectedSignature = base64_encode(
openssl_encrypt(
json_encode($payload),
'AES-256-CBC',
$yourPublicKey,
0,
$yourSecretKey
)
);
if (hash_equals($expectedSignature, $receivedSignature)) {
// signature valid, process the event
http_response_code(200);
}$payload = json_decode($request->getContent(), true);
$signature = $payload['signature'];
unset($payload['signature']);
// decrypt the signature back into the original JSON string
$decrypted = openssl_decrypt(
base64_decode($signature), // undo the outer base64 layer
'AES-256-CBC',
$yourPublicKey,
0, // options = 0, openssl expects base64 input
$yourSecretKey
);
if ($decrypted === json_encode($payload)) {
// signature valid, process the event
http_response_code(200);
}Topup Webhooks
Every time a top-up order's status changes, eGiftly sends a webhook to the same webhook URL configured on your account used for Code Webhooks — same delivery, retry, and AES-256-CBC signature mechanism, just a different event namespace and payload shape. Unlike Code Webhooks (one event per code), a single top-up order can generate several events as it moves through the pipeline — e.g. topup.pending → topup.processing → topup.completed — so expect more than one call per topupOrderId and always key off status, not just receipt of an event.
Events
| Event | Order status | Meaning |
|---|---|---|
| topup.pending | pending | Order created, wallet not yet charged |
| topup.processing | processing | Wallet charged, purchase submitted to the supplier |
| topup.completed | completed | Supplier confirmed the top-up was delivered |
| topup.failed | failed | The purchase failed — see failureReason |
| topup.refunded | refunded | The charge was reversed back to your wallet |
| topup.unknown | unknown | The supplier request timed out or failed to respond — outcome undetermined, held for reconciliation |
Payload fields
| Field | Type | Description |
|---|---|---|
| event | string | One of the events listed above |
| eventReference | integer | Unique reference for this event — use it to deduplicate retries |
| topupOrderId | integer | eGiftly top-up order ID (same as id in Create Topup / Topup Status) |
| vendorOrderUniqueId | string | The customer_reference you sent when creating the top-up |
| denominationId | integer | Top-up denomination ID that was purchased |
| status | string | Current order status — same values as data.status in Topup Status |
| price | number | Amount charged to your wallet for this order |
| currency | string | ISO currency code |
| failureReason | string|null | Present only on topup.failed / topup.unknown. A fixed, generic message — it never includes internal or supplier-specific error text |
| completedAt | string|null | Completion timestamp, or null until resolved |
| date | string | Event timestamp (ISO 8601) |
| signature | string | Same AES-256-CBC signature scheme as Code Webhooks — see Verifying the Signature |
{
"event": "topup.completed",
"eventReference": 15201,
"topupOrderId": 9001,
"vendorOrderUniqueId": "CUST-TOPUP-001",
"denominationId": 301,
"status": "completed",
"price": 10.49,
"currency": "USD",
"failureReason": null,
"completedAt": "2026-08-20T14:20:42.000000Z",
"date": "2026-08-20T14:20:42.000000Z",
"signature": "b64EncodedSignatureString=="
}{
"event": "topup.failed",
"eventReference": 15202,
"topupOrderId": 9002,
"vendorOrderUniqueId": "CUST-TOPUP-002",
"denominationId": 301,
"status": "failed",
"price": 10.49,
"currency": "USD",
"failureReason": "The top-up request could not be completed.",
"completedAt": null,
"date": "2026-08-20T14:22:10.000000Z",
"signature": "b64EncodedSignatureString=="
}{
"event": "topup.processing",
"eventReference": 15200,
"topupOrderId": 9001,
"vendorOrderUniqueId": "CUST-TOPUP-001",
"denominationId": 301,
"status": "processing",
"price": 10.49,
"currency": "USD",
"failureReason": null,
"completedAt": null,
"date": "2026-08-20T14:20:05.000000Z",
"signature": "b64EncodedSignatureString=="
}Range Webhooks
A range order's code is delivered via the same order.code.ready / order.code.refunded events as Code Webhooks — same webhook URL, delivery, retry, and AES-256-CBC signature mechanism. The payload shape differs in two ways worth knowing before you write a handler that expects a fixed-denomination order: orderId and eventReference are the opaque range order id (e.g. RG-20260909162409HU56), and the code/refund object carries denominationCode (the opaque range denomination code) instead of a raw denominationId.
Payload fields
| Field | Type | Description |
|---|---|---|
| event | string | order.code.ready or order.code.refunded |
| eventReference | string | Same value as orderId below — use it to deduplicate retries |
| orderId | string | Opaque range order ID (same as orderId in Create Range Order / Range Order Status) |
| vendorOrderUniqueId | string | The uniqueOrderId you sent when creating the order |
| code.denominationCode | string | Opaque range denomination code (same as code in Range Denominations) — present on order.code.ready, under refund instead on order.code.refunded |
| code.code | string | The redeemable voucher code — only on order.code.ready |
| refund.refundedAmount | number | Amount refunded to your wallet — only on order.code.refunded |
| pendingCodesRemaining | integer | Always 0 for range orders — one order produces exactly one code |
| date | string | Event timestamp (ISO 8601) |
| signature | string | Same AES-256-CBC signature scheme as Code Webhooks — see Verifying the Signature |
{
"event": "order.code.ready",
"eventReference": "RG-20260909162409HU56",
"orderId": "RG-20260909162409HU56",
"vendorOrderUniqueId": "CUST-RANGE-001",
"code": {
"brandName": "Binance",
"brandId": 103,
"denominationName": "Binance 1 - 500 USDT Open Amount",
"denominationCode": "RD-1MUZNII9",
"code": "BMD49AHC7WX8EV66",
"serialNumber": "",
"expiryDate": null,
"createdAt": "2026-09-09T16:24:45.000000Z"
},
"pendingCodesRemaining": 0,
"date": "2026-09-09T16:24:45.000000Z",
"signature": "b64EncodedSignatureString=="
}{
"event": "order.code.refunded",
"eventReference": "RG-20260909162409HU56",
"orderId": "RG-20260909162409HU56",
"vendorOrderUniqueId": "CUST-RANGE-001",
"refund": {
"brandName": "Binance",
"brandId": 103,
"denominationName": "Binance 1 - 500 USDT Open Amount",
"denominationCode": "RD-1MUZNII9",
"refundedAmount": 10.40,
"orderFullyRefunded": true
},
"pendingCodesRemaining": 0,
"date": "2026-09-09T16:24:45.000000Z",
"signature": "b64EncodedSignatureString=="
}