Browse a gift card catalog, place orders against your prepaid balance, and track those orders. This reference covers v2 of the Cheers Gifting API.
https://sandbox.api.cheers.giftContact your Cheers account manager for a sandbox API key.
| Concept | Detail |
|---|---|
| Choose when the card is issued | issuanceType on order creation: get a redeem link now, or the card itself |
| Order by brand and amount | Orders take a brandId and an amount instead of a product identifier |
| Opaque brand identifiers | brandId is an opaque string — see Brand identifiers |
| Read your orders | GET /api/v2/order/{orderId} returns the same shape as order creation |
| Retry-safe order creation | Reusing an idempotency key replays or resumes the order instead of failing |
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v2/catalog | Get catalog |
| POST | /api/v2/order | Create an order |
| GET | /api/v2/order/{orderId} | Get an order |
| GET | /api/v2/balance | Get balance |
| GET | /api/v2/order/transactions | List transactions |
Every endpoint requires an API key, sent in the X-API-KEY request header. The key identifies your tenant — all catalogs, balances, orders, and transactions are scoped to it automatically.
X-API-KEY: your-api-key-hereA request with a missing or invalid key returns 401 Unauthorized.
1. Find a brand. Fetch the catalog for the currency you want to spend in, then pick a brandId and one of its denominations.
curl -X GET "https://sandbox.api.cheers.gift/api/v2/catalog?currency=USD" \
-H "X-API-KEY: your-api-key"2. Create the order. Choose how you want it issued.
curl -X POST "https://sandbox.api.cheers.gift/api/v2/order" \
-H "X-API-KEY: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"brandId": "bmctMTpjYXQtMTIzNDU",
"amount": 25.00,
"idempotencyKey": "3f9a1c7e-1d2b-4f8a-9c31-0f4b2d6a77e1",
"issuanceType": "OnDemand"
}'3. Deliver the gift. For OnDemand you get redeem.url — hand that to your customer. For JustInTime you get card with the card details themselves.
| Topic | Detail |
|---|---|
| Versioning | The version is part of the path: /api/v{version}/…. This document covers v2. |
| Content type | Request bodies are JSON; send Content-Type: application/json. Responses are JSON. |
| Field naming | JSON fields are camelCase. |
| Enums | Sent and returned by name (for example "OnDemand"). Numeric values are also accepted on input. |
| Currencies | ISO 4217 three-letter codes (USD, EUR, GBP). Codes are case-insensitive and normalized to uppercase. |
| Amounts | Decimal numbers in the currency's major unit — 25.00 means 25 dollars, not 25 cents. |
| Timestamps | ISO 8601, UTC. |
| Errors | Non-2xx responses carry a JSON body with a message field. See Error handling. |
In v2, brandId is an opaque string. Read it from the catalog and send it back unchanged when you create an order.
Its internal format is not part of this contract and will change. Treat it the way you would treat a session token.
issuanceType decides when the gift card is issued.
| Value | Behaviour | You receive |
|---|---|---|
| OnDemand | The card is issued later, when the recipient opens the redeem link | redeem.url |
| JustInTime | The card is issued immediately | card with the card details |
OnDemand returns as soon as the order is paid for. It is the right choice when you are sending a link to a recipient and do not need the card yourself.
JustInTime waits for the card to be issued, which involves two asynchronous steps on our side. It usually completes within the request. When it does not, you get status: "Processing" and a retryAfter — the order is fine, it is simply not finished yet. See Handling Processing.
/api/v2/catalogReturns the brands available to your tenant for a given currency, with the amounts each can be ordered at.
| Header | Required | Description |
|---|---|---|
| X-API-KEY | Yes | Your API key |
| Parameter | Type | Required | Description |
|---|---|---|---|
| currency | string | Yes | ISO 4217 currency code (e.g. USD, EUR, GBP) |
{
"catalogId": "Y2F0LTEyMzQ1",
"currency": "USD",
"brands": [
{
"brandId": "bmctMTpjYXQtMTIzNDU",
"name": "Example Brand",
"imageUrl": "https://cdn.progifts.io/brands/77.png",
"thumbnailUrl": "https://cdn.progifts.io/brands/77-thumb.png",
"cardType": "ClosedLoop",
"denominations": [25.00, 50.00, 100.00],
"ranges": []
}
]
}| Field | Type | Description |
|---|---|---|
| catalogId | string | Opaque identifier of the catalog |
| currency | string | ISO 4217 currency code of this catalog |
| brands | array | Brands available to your tenant in this currency |
| Field | Type | Description |
|---|---|---|
| brandId | string | Opaque brand identifier — see Brand identifiers |
| name | string | Display name of the brand |
| imageUrl | string | URL of the brand's card image (nullable) |
| thumbnailUrl | string | URL of the brand's thumbnail image (nullable) |
| cardType | string | ClosedLoop, OpenLoop or Multicard (nullable) |
| denominations | array | The exact amounts this brand can be ordered at |
| ranges | array | Open amount ranges. Currently always empty — see below |
| Field | Type | Description |
|---|---|---|
| min | decimal | Lowest orderable amount |
| max | decimal | Highest orderable amount |
| increment | decimal | Step between valid amounts (nullable) |
ranges is always empty today — every brand uses fixed denominations. It is present so that brands accepting an open amount can be added without a breaking change. Handle it if you can; ignoring it is safe for now.
| Status | When |
|---|---|
| 400 | currency is missing or empty |
| 401 | Missing or invalid API key |
curl -X GET "https://sandbox.api.cheers.gift/api/v2/catalog?currency=USD" \
-H "X-API-KEY: your-api-key"/api/v2/orderCreates a gift card order, pays for it from your tenant balance, and returns either a redeem link or the issued card, depending on issuanceType.
| Header | Required | Description |
|---|---|---|
| X-API-KEY | Yes | Your API key |
| Content-Type | Yes | application/json |
{
"brandId": "bmctMTpjYXQtMTIzNDU",
"amount": 25.00,
"idempotencyKey": "3f9a1c7e-1d2b-4f8a-9c31-0f4b2d6a77e1",
"issuanceType": "JustInTime",
"metadata": {
"externalCustomerId": "customer-abc-001"
}
}| Field | Type | Required | Description |
|---|---|---|---|
| brandId | string | Yes | Opaque brand identifier from the catalog. Max 500 characters |
| amount | decimal | Yes | The gift card's value. Must be one of the brand's denominations. Some brands add a fee on top — see Fees |
| idempotencyKey | string | Yes | Your unique key for this order. Max 500 characters; must be unique per tenant. See Idempotency |
| issuanceType | string | Yes | OnDemand or JustInTime — see Issuance types |
| metadata | object | No | Optional extra data to record against the order |
| metadata.externalCustomerId | string | No | Your own identifier for the customer. Max 500 characters |
{
"orderId": 987654,
"idempotencyKey": "3f9a1c7e-1d2b-4f8a-9c31-0f4b2d6a77e1",
"issuanceType": "OnDemand",
"status": "Completed",
"amount": 25.00,
"currency": "USD",
"redeem": {
"url": "https://redeem.progifts.io/o/abc123"
}
}| Field | Type | Description |
|---|---|---|
| orderId | integer | Unique identifier of the created order |
| idempotencyKey | string | The key you sent, echoed back |
| issuanceType | string | The issuance type of this order |
| status | string | Completed or Processing — see Order status |
| amount | decimal | The gift card's value — the amount you requested. Not the total billed; see Fees |
| currency | string | ISO 4217 currency code |
| redeem | object | Present for a completed OnDemand order |
| redeem.url | string | URL the recipient uses to redeem the gift card |
| card | object | Present for a completed JustInTime order — see Card |
| retryAfter | date-time | Present only while status is Processing |
| Status | When |
|---|---|
| 400 | Unknown or malformed brandId, amount is not one of the brand's denominations, the brand's currency is not enabled for your tenant, or issuanceType: "JustInTime" was requested for a Multicard brand |
| 401 | Missing or invalid API key |
| 409 | The idempotencyKey was already used with a different request body |
| 502 | The payment could not be completed downstream |
| 500 | Unexpected server error |
curl -X POST "https://sandbox.api.cheers.gift/api/v2/order" \
-H "X-API-KEY: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"brandId": "bmctMTpjYXQtMTIzNDU",
"amount": 25.00,
"idempotencyKey": "3f9a1c7e-1d2b-4f8a-9c31-0f4b2d6a77e1",
"issuanceType": "JustInTime",
"metadata": { "externalCustomerId": "customer-abc-001" }
}'/api/v2/order/{orderId}Returns one of your orders, in exactly the same shape as Create an order.
Safe to poll: it checks for progress at most once per call and never blocks. It is a pure read — to move a Processing order forward, re-POST it. See Handling Processing.
| Header | Required | Description |
|---|---|---|
| X-API-KEY | Yes | Your API key |
| Parameter | Type | Required | Description |
|---|---|---|---|
| orderId | integer | Yes | The orderId returned by Create an order |
Identical to the create-order response.
| Status | When |
|---|---|
| 401 | Missing or invalid API key |
| 404 | No such order for your tenant. Orders belonging to another tenant are reported as 404, not 403 |
| 502 | The payment could not be completed downstream |
curl -X GET "https://sandbox.api.cheers.gift/api/v2/order/987654" \
-H "X-API-KEY: your-api-key"/api/v2/balanceReturns your tenant's prepaid wallet balance. Orders are funded from this balance, so check it before placing large batches.
| Header | Required | Description |
|---|---|---|
| X-API-KEY | Yes | Your API key |
| Parameter | Type | Required | Description |
|---|---|---|---|
| currencyCode | string | Yes | ISO 4217 currency code to report the balance in |
{
"partnerId": "1042",
"balances": [
{
"currency": "USD",
"amount": 1520.75
}
]
}| Field | Type | Description |
|---|---|---|
| partnerId | string | Your tenant/partner identifier |
| balances | array | Wallet balances |
| Field | Type | Description |
|---|---|---|
| currency | string | ISO 4217 currency code |
| amount | decimal | Available amount in that currency |
| Status | When |
|---|---|
| 400 | currencyCode is missing or empty |
| 401 | Missing or invalid API key |
| 404 | No balance exists for the requested currency |
| 500 | Unexpected server error |
curl -X GET "https://sandbox.api.cheers.gift/api/v2/balance?currencyCode=USD" \
-H "X-API-KEY: your-api-key"/api/v2/order/transactionsReturns your tenant's orders as a paged, date-filtered list — useful for reconciliation and reporting.
| Header | Required | Description |
|---|---|---|
| X-API-KEY | Yes | Your API key |
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| page | integer | No | 1 | Page number, starting at 1 |
| pageSize | integer | No | 10 | Items per page. Between 1 and 100 |
| orderByAsc | boolean | No | false | Sort oldest-first when true, newest-first when false |
| startDate | date-time | No | endDate minus the history window | Start of the period (inclusive) |
| endDate | date-time | No | now (UTC) | End of the period (inclusive) |
History is limited to a rolling window — one year by default. A startDate older than that window is rejected with 400, and endDate must not be earlier than startDate.
{
"page": 1,
"pageSize": 10,
"totalCount": 42,
"totalPages": 5,
"items": [
{
"orderId": "987654",
"storeName": "Example Store",
"storeId": 321,
"productId": 12345,
"price": 25.00,
"currency": "USD",
"timestamp": "2026-08-05T09:31:44Z",
"orderStatus": "Committed",
"metaData": null
}
]
}| Field | Type | Description |
|---|---|---|
| page | integer | The page that was returned |
| pageSize | integer | Items per page |
| totalCount | integer | Total transactions matching the filter |
| totalPages | integer | Total number of pages |
| items | array | The transactions on this page |
| Field | Type | Description |
|---|---|---|
| orderId | string | Identifier of the order |
| storeName | string | Brand/store the gift card belongs to (nullable) |
| storeId | integer | Identifier of the store (nullable) |
| productId | integer | The underlying provider product id — not your brandId |
| price | decimal | Amount charged, fees included. This is where you see the order's billed total — the order response itself reports only the requested amount |
| currency | string | ISO 4217 currency code |
| timestamp | date-time | When the transaction took place |
| orderStatus | string | Status name — see Transaction status (nullable) |
| metaData | string | Additional order metadata (nullable) |
| Status | When |
|---|---|
| 400 | page < 1, pageSize outside 1–100, or an invalid/out-of-window date range |
| 401 | Missing or invalid API key |
| 500 | Unexpected server error |
curl -X GET "https://sandbox.api.cheers.gift/api/v2/order/transactions?page=1&pageSize=25&orderByAsc=false&startDate=2026-01-01&endDate=2026-08-05" \
-H "X-API-KEY: your-api-key"JustInTime issuance waits on two asynchronous steps. Most orders finish inside the original request. When one does not, you get:
{
"orderId": 987654,
"status": "Processing",
"retryAfter": "2026-08-12T09:15:05Z"
}The order exists and is paid for. Wait until retryAfter, then re-POST the order — same body, same idempotencyKey:
curl -X POST "https://sandbox.api.cheers.gift/api/v2/order" \
-H "X-API-KEY: your-api-key" -H "Content-Type: application/json" \
-d '{ ...exactly the same body, including the same idempotencyKey... }'This resumes the existing order — it never creates a second one — and waits for the card.
GET /api/v2/order/{orderId} is a read: it reports the current state and is safe to call as often as you like, but it does not itself advance a stalled order. Use it to check; use the re-POST to make progress.
amount is the gift card's value — what the recipient receives, and what you asked for. It is not necessarily what you are billed.
Some brands carry a processing fee, applied by the platform on top of the card value. A $50 card on a brand with a $4.90 fee draws $54.90 from your balance while amount still reads 50.00.
Create an order is the only endpoint that moves money, so it is guarded by an idempotency key that you generate and send in idempotencyKey.
Reusing a key with the same request body returns 200 OK — it replays the original order, or resumes it if it was left unfinished. Reusing a key with a different request body returns 409 Conflict.
So a retry after a timeout simply returns the order — you never need to reconcile a 409 to find out whether the first attempt succeeded.
{ "message": "Duplicate request detected. This idempotency key has already been used." }| Value | Meaning |
|---|---|
| Completed | The redeem link (OnDemand) or the card (JustInTime) is ready |
| Processing | Issuance has not finished yet. Retry after retryAfter |
orderStatus on List transactions — a separate vocabulary from Order status above, describing the payment transaction rather than issuance.
| Value | Meaning |
|---|---|
| Authorized | Funds authorized, order not yet finalized |
| Committed | Order completed successfully |
| Voided | Order cancelled before completion |
| Refunded | Order refunded |
| ErrorAuthorizing | Authorization failed |
| ErrorCommitting | Commit failed |
| ErrorRefunding | Refund failed |
Returned as card on a completed JustInTime order.
| Field | Type | Description |
|---|---|---|
| redemptionType | string | Code or Url — how the card is redeemed |
| redemptionValue | string | The card number, or the redemption URL |
| verificationCodeType | string | PinCode, SecurityCode, SecretCode or Token (nullable) |
| verificationCode | string | The accompanying code, when the card has one (nullable) |
| expiresAt | date-time | When the card expires (nullable) |
| issuedAt | date-time | When the card was issued |
There is no Barcode redemption type. A card displayed as a barcode is a Code — the barcode is a way of presenting the value, not a separate way of redeeming it.
| Value | Meaning |
|---|---|
| ClosedLoop | Redeemable with one brand |
| OpenLoop | Redeemable anywhere the card network is accepted |
| Multicard | A card covering a group of brands |
The API uses standard HTTP status codes.
| Status | Meaning |
|---|---|
| 200 | Success — including status: "Processing" |
| 400 | Bad Request — unknown brandId, invalid amount, or unsupported currency |
| 401 | Unauthorized — missing or invalid API key |
| 404 | Not Found — no such order for your tenant |
| 409 | Conflict — the idempotency key was reused with a different request |
| 500 | Internal Server Error |
| 502 | Bad Gateway — the payment could not be completed downstream |
Error responses carry a JSON body with a human-readable message:
{ "message": "Amount 37 is not available for brand 'bmctMTpjYXQtMTIzNDU'. Valid denominations: 25, 50, 100." }Log the message verbatim when reporting an issue to Jifiti support, along with the request timestamp (UTC) and, where relevant, the idempotencyKey or orderId.
| Status | Retry? |
|---|---|
| 400, 401, 404 | No — fix the request or the key first |
| 409 | No — the key was reused with a different body. Use a new key, or resend the original body |
| 500 | Yes, with exponential backoff, reusing the same idempotencyKey |
| 502 | No — the payment failed. Contact support with the orderId |
Need a sandbox key? Talk to our team.