Developer docs
Two API surfaces: the tenant API (issue and redeem cards under your brand) and the retailer POS API (your till software activates consigned cards directly). Both are Bearer-key authenticated, JSON in and out.
Tenant API keys look like gft_… and carry one of two scopes:
Authorization: Bearer gft_xxxxxxxxxxxxxxxxxxxxxxxx Retailer POS keys look like gfr_… and scope to a single shop:
Authorization: Bearer gfr_xxxxxxxxxxxxxxxxxxxxxxxxRead-only preview — doesn't consume the card. redeem scope.
POST /api/v1/validations
{
"code": "XXXXXXXXXXXXXXXX",
"externalUserRef": "user_123" // or send X-End-User-Ref header
}
200 OK
{
"valid": true,
"serial": "AB12-CD34",
"productRef": "gift-6m",
"faceValue": 1000,
"status": "active"
} Unknown, invalid, and expired codes all return the identical { valid: false, serial: null, productRef: null, faceValue: null, status: null } shape — the API never leaks whether a code exists.
One call flips the card and records the redemption atomically. Idempotent on idempotencyKey — safe to retry a timed-out request.
POST /api/v1/redemptions
{
"code": "XXXXXXXXXXXXXXXX",
"idempotencyKey": "order_9f3a...",
"externalUserRef": "user_123",
"confirm": true // optional; omit or true = one-phase (default)
}
201 Created
{
"id": "rdm_...",
"serial": "AB12-CD34",
"productRef": "gift-6m",
"faceValue": 1000,
"status": "completed",
"reservedUntil": null,
"redeemedAt": "2026-08-07T12:00:00.000Z"
}Two-phase mode — pass "confirm": false to reserve the card (10-minute TTL) before you've actually granted value, then confirm once you have:
POST /api/v1/redemptions/:id/confirm
200 OK
{
"id": "rdm_...",
"serial": "AB12-CD34",
"productRef": "gift-6m",
"faceValue": 1000,
"status": "completed",
"reservedUntil": null,
"redeemedAt": "2026-08-07T12:00:00.000Z"
} A reservation that's never confirmed expires on its own and the card returns to active. Replaying the same idempotencyKey with the same code always returns the original result; reusing it with a different code is a 409.
GET /api/v1/products
200 OK
{
"products": [
{ "ref": "gift-6m", "label": "6-month card", "durationMonths": 6, "faceValue": 1000, "currency": "USD" }
]
}Unauthenticated, cacheable — poll this to degrade gracefully instead of hard-failing your redeem page.
GET /api/v1/status
200 OK
{ "status": "ok", "time": "2026-08-07T12:00:00.000Z" }For a shop's own till software — activates a card consigned to that reseller. Same idempotency contract as redemptions.
POST /api/ra/v1/activations
{
"serial": "AB12-CD34",
"cashierRef": "till-04",
"idempotencyKey": "sale_9f3a...",
"customerPhone": "+94771234567", // optional, for receipt
"customerEmail": "buyer@example.com" // optional, for receipt
}
201 Created
{
"ok": true,
"serial": "AB12-CD34",
"productRef": "gift-6m",
"faceValue": 1000,
"currency": "USD",
"activatedAt": "2026-08-07T12:00:00.000Z"
}Reconcile what you recorded against what we recorded — a simple cursor over recent activations for this key's shop:
GET /api/ra/v1/activations?since=2026-08-01T00:00:00Z&limit=100
200 OK
{
"activations": [
{
"idempotencyKey": "sale_9f3a...",
"cashierRef": "till-04",
"serial": "AB12-CD34",
"productRef": "gift-6m",
"faceValue": 1000,
"currency": "USD",
"activatedAt": "2026-08-07T12:00:00.000Z",
"recordedAt": "2026-08-07T12:00:00.100Z"
}
],
"nextSince": "2026-08-07T12:00:00.100Z"
} Pass customerPhone and/or customerEmail on activation and the buyer gets an SMS/email receipt automatically — no separate call needed. This never blocks or fails the sale itself.
Standard HTTP status codes. On the redeem-scope endpoints, unknown and invalid codes are deliberately indistinguishable — same shape, same timing — so a client integration can never be used to probe which codes exist.
Need a key, or more detail on any of this? Email gifts@developeroffice.com.