API and Webhook API and Webhook
REST API (server-to-server) và Webhook cho tích hợp backend: đọc/ghi user, điểm, tier, leaderboard; nhận sự kiện khi đổi thưởng, thay đổi tier.
Giá trị cho đối tác (use case)
Backend của bạn có thể đọc điểm, hạng, bảng xếp hạng của bất kỳ user nào (bằng external_id) để hiển thị trên app, gửi email, hoặc quyết định ưu đãi. Khi user đổi thưởng, Engine gửi webhook sang URL của bạn — bạn nhận payload và fulfill (gửi mã voucher, kích hoạt quà). Một API Key, vài endpoint, là đủ để đồng bộ dữ liệu và tự động hóa fulfillment.
Tích hợp từng bước (cho Developer)
- Lấy API Key (Bearer token)
Từ Client Admin → API Keys. Mọi request gửi header
Authorization: Bearer <API_KEY>. - Đọc dữ liệu user bằng external_id
Backend của bạn có thể đọc điểm, hạng (tier), bảng xếp hạng (leaderboard) của user thông qua
external_id. Nếu user chưa tồn tại, cần tạo/xác định user (Identify) trước. Chi tiết kỹ thuật (endpoint, request/response) do bên vận hành GamifyEngine cung cấp cho đối tác tích hợp. - Webhook (khi user đổi thưởng)
Cấu hình URL fulfillment trong Client Admin. Engine gửi POST tới URL của bạn khi có redemption; bạn xử lý và trả 2xx. Có thể dùng HMAC để verify.
REST API (server-to-server)
- Xác thực: API Key (Bearer). Lấy từ Client Admin → API Keys.
- Định danh user:
external_id(id user trên hệ thống của bạn). - Bạn có thể đọc điểm, hạng (tier), leaderboard của user; tạo/xác định user (Identify) nếu chưa có. Định dạng request/response và URL cụ thể do bên vận hành cung cấp trong tài liệu tích hợp dành cho đối tác.
Webhook
Engine gửi HTTP request (POST) tới URL mà bạn cấu hình khi có sự kiện (vd. reward_redeemed, tier_change). Payload thường là JSON chứa id redemption, user, reward, trạng thái. Xác thực: có thể dùng HMAC (signature trong header); Engine retry tối đa 3 lần nếu endpoint trả lỗi hoặc timeout.
Redemption callback (đổi thưởng)
Khi user đổi điểm lấy reward, Engine gửi webhook tới URL của bạn. Bạn xử lý (gửi quà, kích hoạt mã) và trả 2xx. Sau đó gọi Callback URL (Fulfillment API) để cập nhật trạng thái fulfilled / failed. Thông tin sau khớp với màn hình Client Admin → Information for third parties (khi bên thứ ba trả voucher hoặc báo trạng thái).
Callback URL: <BASE_URL>/v1/admin/rewards/redemptions/{redemption_id} — thay {redemption_id} bằng redemption_id nhận từ body webhook. Method: PATCH. Xác thực: header Authorization: Bearer <api_key> (tạo API Key trong Client Admin → API Keys, chia sẻ cho bên thứ ba; key chỉ hiển thị một lần khi tạo).
Bạn cần làm gì (checklist cho Developer)
- Lấy API Key — Client Admin → API Keys. Dùng cho mọi request tới Engine (Identify, đọc điểm, gọi callback).
- Cấu hình URL webhook — Client Admin → mục Webhook / cài đặt. Nhập URL endpoint backend của bạn (vd.
https://api.tenant.com/webhooks/gamify). Lưu secret (nếu có) để verify chữ ký. - Implement endpoint nhận webhook — Một route POST nhận JSON, parse body, trả 200 OK trong vài giây (nếu xử lý lâu thì queue rồi trả 200 ngay). Nếu không trả 2xx, Engine có thể retry.
- (Khuyến nghị) Verify chữ ký — Request có header
X-GamifyEngine-SignaturevàX-GamifyEngine-Timestamp. Engine ký chuỗitimestamp + '.' + bodyRaw(body là raw string của request, chưa parse JSON). Bạn dùng cùng cách: lấy raw body và timestamp từ header, nốitimestamp + '.' + bodyRaw, HMAC-SHA256 với secret, so sánh vớiX-GamifyEngine-Signature. Xem mục “Verify HMAC” bên dưới. - Fulfill — Từ payload lấy
external_user_id,redemption_id,reward→ thực hiện cấp quà (gửi voucher, nạp credit, ghi log). - Gọi API callback — Sau khi fulfill xong (hoặc thất bại), gọi
PATCH <BASE_URL>/v1/admin/rewards/redemptions/{redemption_id}với headerAuthorization: Bearer <api_key>. Body: xem mục “Ví dụ body khi gọi API callback” bên dưới. URL đầy đủ và API Key cũng có trong Client Admin (Information for third parties).
Ví dụ payload webhook (bạn sẽ nhận)
Engine gửi POST tới URL của bạn với body JSON dạng sau. Dùng redemption_id khi gọi API callback sau này.
{
"event": "reward_redeemed",
"redemption_id": "e6b49abc-c19e-4c39-b27d-19f22fb0bdae",
"user_id": "57b96ba1-1eb9-458f-8126-34b6d54292e7",
"external_user_id": "usr_abc123",
"reward_id": "084d33ab-db85-4210-8293-011fa5c35800",
"reward_type": "bonus_credit",
"reward_value": 10,
"points_spent": 10000,
"reward": {
"id": "084d33ab-db85-4210-8293-011fa5c35800",
"name": "Voucher 10K",
"type": "bonus_credit",
"fulfillmentData": {}
},
"timestamp": "2026-03-01T04:11:00.763Z"
}
Headers: Content-Type: application/json, X-GamifyEngine-Signature, X-GamifyEngine-Timestamp (dùng để verify).
Verify chữ ký HMAC
Format: Engine gửi X-GamifyEngine-Timestamp (giá trị timestamp nguyên bản, ví dụ Unix seconds hoặc chuỗi do Engine quy định) và X-GamifyEngine-Signature = HMAC-SHA256(timestamp + '.' + bodyRaw, secret). bodyRaw phải là raw body của HTTP request (chuỗi byte/string gốc), không phải JSON đã parse rồi stringify lại (vì thứ tự key có thể đổi). Trong Express, mặc định express.json() đã parse body nên bạn mất raw — cần dùng middleware lưu raw body (vd. express.raw({ type: 'application/json' }) cho route webhook) hoặc đọc raw từ stream trước khi parse.
Ví dụ code backend (Node.js / Express)
Endpoint nhận webhook: (lưu raw body để verify), parse body, verify signature, fulfill, trả 200. Sau đó có thể gọi API callback với redemption_id và status / fulfilled_at / fulfillment_data.
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.GAMIFY_WEBHOOK_SECRET; // secret cấu hình trong Client Admin
// Verify chữ ký (nếu có secret)
function verifySignature(bodyRaw, signature, timestamp) {
if (!WEBHOOK_SECRET) return true;
const payload = timestamp + '.' + bodyRaw;
const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
app.post('/webhooks/gamify', (req, res) => {
const rawBody = JSON.stringify(req.body);
const sig = req.headers['x-gamifyengine-signature'];
const ts = req.headers['x-gamifyengine-timestamp'];
if (!verifySignature(rawBody, sig, ts)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { event, redemption_id, external_user_id, reward, points_spent } = req.body;
if (event !== 'reward_redeemed') {
return res.status(200).json({ ok: true });
}
// Fulfill: gửi voucher / nạp credit / ...
yourFulfillService.fulfill({
redemptionId: redemption_id,
externalUserId: external_user_id,
rewardName: reward?.name,
rewardType: reward?.type,
pointsSpent: points_spent,
})
.then((result) => {
// (Tuỳ chọn) Gọi API callback để cập nhật fulfilled
// await updateRedemptionFulfillment(redemption_id, { status: 'fulfilled', fulfilled_at: new Date().toISOString(), fulfillment_data: { code: result.voucherCode } });
res.status(200).json({ ok: true });
})
.catch((err) => {
// Trả 200 để Engine không retry ngay; có thể gọi callback status: 'failed'
res.status(200).json({ ok: true, note: 'fulfill queued or failed' });
});
});
yourFulfillService.fulfill(...) là logic của bạn (gửi email mã voucher, cộng credit vào tài khoản user, v.v.). Sau khi fulfill xong, gọi API callback (URL và format do operator cung cấp) với redemption_id, status: 'fulfilled' hoặc 'failed', fulfilled_at, fulfillment_data (vd. { "code": "VOUCHER-ABC123" }).
Ví dụ body khi gọi API callback (sau khi fulfill)
Gửi PATCH tới <BASE_URL>/v1/admin/rewards/redemptions/{redemption_id} với header Authorization: Bearer <API_KEY>, Content-Type: application/json. Body mẫu (chia sẻ với đối tác):
Thành công (có mã voucher):
{
"status": "fulfilled",
"fulfilled_at": "2026-02-24T15:00:00Z",
"fulfillment_data": { "code": "VOUCHER-ABC123" }
}
Thất bại:
{ "status": "failed", "fulfillment_data": { "reason": "Out of stock" } }
redemption_id lấy từ webhook body (field redemption_id).
Mã lỗi thường gặp
Khi gọi API hoặc nhận webhook, có thể gặp các mã sau. Xử lý ngắn gọn:
| Mã | Ý nghĩa | Cách xử lý |
|---|---|---|
AUTH_MISSING_KEY | Thiếu API Key | Gửi header Authorization: Bearer <api_key>. |
AUTH_INVALID_KEY | API Key sai hoặc đã thu hồi | Kiểm tra key, tạo key mới trong Client Admin → API Keys nếu cần. |
USER_NOT_FOUND | User chưa tồn tại | Gọi Identify (POST /v1/users với externalId) trước khi đọc điểm/tier hoặc gửi event. |
TENANT_QUERY_MISMATCH | Tham số tenant không khớp API Key | Đảm bảo query tenant (hoặc header) trùng với tenant của API Key. |
EVENT_TYPE_NOT_DECLARED | Event type chưa được khai báo | Tạo event type trong Client Admin → Event Types trước khi gửi event. |
CORS_NOT_ALLOWED_FOR_TENANT | Origin không nằm trong CORS của tenant | Thêm origin (URL frontend) vào Client Admin → Settings → CORS (Allowed origins). |
Đọc điểm/tier qua REST: định dạng request/response và URL cụ thể do bên vận hành cung cấp trong tài liệu tích hợp. Nếu cần, tham khảo thêm tài liệu từ operator.
REST API (server-to-server) and Webhooks for backend integration: read/write users, points, tier, leaderboard; receive events on reward redemption, tier change.
Value for partners (use case)
Your backend can read points, tier, leaderboard for any user (by external_id) to show in the app, send emails, or decide offers. When a user redeems a reward, the Engine sends a webhook to your URL — you receive the payload and fulfill (send voucher code, activate gift). One API Key and a few endpoints are enough to sync data and automate fulfillment.
Step-by-step integration (for developers)
- Get API Key (Bearer token)
From Client Admin → API Keys. Send header
Authorization: Bearer <API_KEY>on every request. - Read user data by external_id
Your backend can read points, tier, and leaderboard for a user via
external_id. If the user does not exist yet, create/identify the user (Identify) first. Technical details (endpoints, request/response) are provided by the GamifyEngine operator to integrating partners. - Webhook (on reward redemption)
Configure your fulfillment URL in Client Admin. The Engine POSTs to your URL on redemption; you process and return 2xx. Optionally verify with HMAC.
REST API (server-to-server)
- Auth: API Key (Bearer). Obtain from Client Admin → API Keys.
- User identity:
external_id(your system’s user id). - You can read user points, tier, and leaderboard; create/identify the user (Identify) if needed. Request/response format and exact URLs are provided by the operator in the integration documentation for partners.
Webhook
The Engine sends HTTP requests (POST) to the URL you configure when events occur (e.g. reward_redeemed, tier_change). The payload is typically JSON with redemption id, user, reward, status. Verification: you can use HMAC (signature in header); the Engine retries up to 3 times on failure or timeout.
Redemption callback
When a user redeems, the Engine sends a webhook to your URL. You process (send gift, activate code) and return 2xx. Then call the Callback URL (Fulfillment API) to update status to fulfilled or failed. The details below match Client Admin → Information for third parties (when they return voucher or report status).
Callback URL: <BASE_URL>/v1/admin/rewards/redemptions/{redemption_id} — replace {redemption_id} with the redemption_id from the webhook body. Method: PATCH. Auth: header Authorization: Bearer <api_key> (create an API Key in Client Admin → API Keys and share with the third party; the key is only shown once when created).
What you need to do (Developer checklist)
- Get API Key — Client Admin → API Keys. Use it for all requests to the Engine (Identify, read points, callback).
- Configure webhook URL — Client Admin → Webhook / settings. Enter your backend endpoint URL (e.g.
https://api.tenant.com/webhooks/gamify). Save the secret (if any) for signature verification. - Implement webhook endpoint — A POST route that accepts JSON, parses the body, and returns 200 OK within a few seconds (if processing is slow, queue and return 200 immediately). If you don't return 2xx, the Engine may retry.
- (Recommended) Verify signature — Requests include
X-GamifyEngine-SignatureandX-GamifyEngine-Timestamp. The Engine signs the stringtimestamp + '.' + bodyRaw(body is the raw string of the request, not parsed JSON). You do the same: take the raw body and timestamp from the header, concatenatetimestamp + '.' + bodyRaw, HMAC-SHA256 with secret, compare toX-GamifyEngine-Signature. See “Verify HMAC” below. - Fulfill — From the payload use
external_user_id,redemption_id,rewardto grant the reward (send voucher, credit account, log). - Call callback API — After fulfilling (or on failure), call
PATCH <BASE_URL>/v1/admin/rewards/redemptions/{redemption_id}with headerAuthorization: Bearer <api_key>. Body: see “Example callback body” below. Full URL and API Key are also in Client Admin (Information for third parties).
Example webhook payload (what you receive)
The Engine sends POST to your URL with a JSON body like below. Use redemption_id when calling the callback API later.
{
"event": "reward_redeemed",
"redemption_id": "e6b49abc-c19e-4c39-b27d-19f22fb0bdae",
"user_id": "57b96ba1-1eb9-458f-8126-34b6d54292e7",
"external_user_id": "usr_abc123",
"reward_id": "084d33ab-db85-4210-8293-011fa5c35800",
"reward_type": "bonus_credit",
"reward_value": 10,
"points_spent": 10000,
"reward": {
"id": "084d33ab-db85-4210-8293-011fa5c35800",
"name": "Voucher 10K",
"type": "bonus_credit",
"fulfillmentData": {}
},
"timestamp": "2026-03-01T04:11:00.763Z"
}
Headers: Content-Type: application/json, X-GamifyEngine-Signature, X-GamifyEngine-Timestamp (for verification).
Verify HMAC signature
Format: The Engine sends X-GamifyEngine-Timestamp (raw timestamp value, e.g. Unix seconds or Engine-defined string) and X-GamifyEngine-Signature = HMAC-SHA256(timestamp + '.' + bodyRaw, secret). bodyRaw must be the raw body of the HTTP request (original byte/string), not JSON that was parsed and stringified again (key order may change). In Express, express.json() parses the body by default so you lose the raw body — use middleware to capture the raw body (e.g. express.raw({ type: 'application/json' }) for the webhook route) or read from the stream before parsing.
Example backend code (Node.js / Express)
Webhook endpoint: (capture raw body for verification), parse body, verify signature, fulfill, return 200. Then optionally call the callback API with redemption_id and status / fulfilled_at / fulfillment_data.
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.GAMIFY_WEBHOOK_SECRET;
function verifySignature(bodyRaw, signature, timestamp) {
if (!WEBHOOK_SECRET) return true;
const payload = timestamp + '.' + bodyRaw;
const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
app.post('/webhooks/gamify', (req, res) => {
const rawBody = JSON.stringify(req.body);
const sig = req.headers['x-gamifyengine-signature'];
const ts = req.headers['x-gamifyengine-timestamp'];
if (!verifySignature(rawBody, sig, ts)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { event, redemption_id, external_user_id, reward, points_spent } = req.body;
if (event !== 'reward_redeemed') {
return res.status(200).json({ ok: true });
}
yourFulfillService.fulfill({
redemptionId: redemption_id,
externalUserId: external_user_id,
rewardName: reward?.name,
rewardType: reward?.type,
pointsSpent: points_spent,
})
.then((result) => {
res.status(200).json({ ok: true });
})
.catch((err) => {
res.status(200).json({ ok: true, note: 'fulfill queued or failed' });
});
});
yourFulfillService.fulfill(...) is your logic. After fulfilling, call the callback API (URL from the operator) with redemption_id, status, fulfilled_at, fulfillment_data.
Example callback body (after fulfilling)
Send PATCH to <BASE_URL>/v1/admin/rewards/redemptions/{redemption_id} with headers Authorization: Bearer <API_KEY>, Content-Type: application/json. Sample body (share with partner):
Fulfilled with voucher code:
{
"status": "fulfilled",
"fulfilled_at": "2026-02-24T15:00:00Z",
"fulfillment_data": { "code": "VOUCHER-ABC123" }
}
Failed:
{ "status": "failed", "fulfillment_data": { "reason": "Out of stock" } }
Use the redemption_id from the webhook body.
Common error codes
When calling the API or receiving webhooks, you may see the following codes. Brief handling:
| Code | Meaning | What to do |
|---|---|---|
AUTH_MISSING_KEY | Missing API Key | Send header Authorization: Bearer <api_key>. |
AUTH_INVALID_KEY | Invalid or revoked API Key | Check the key; create a new one in Client Admin → API Keys if needed. |
USER_NOT_FOUND | User does not exist | Call Identify (POST /v1/users with externalId) before reading points/tier or sending events. |
TENANT_QUERY_MISMATCH | Tenant parameter does not match API Key | Ensure the tenant query (or header) matches the API Key’s tenant. |
EVENT_TYPE_NOT_DECLARED | Event type not declared | Create the event type in Client Admin → Event Types before sending events. |
CORS_NOT_ALLOWED_FOR_TENANT | Origin not in tenant CORS list | Add your frontend origin to Client Admin → Settings → CORS (Allowed origins). |
Reading points/tier via REST: request/response format and exact URLs are provided by the operator in the integration doc. Refer to the operator’s documentation if needed.