List Coupons
List the coupons issued under your merchant, newest first, with cursor pagination.
Endpoint
GET /v1/coupons
Headers
| Header | Type | Description | Required |
|---|---|---|---|
X-Client-Id | String | Your client ID | ✅ Yes |
X-Client-Secret | String | Your client secret | ✅ Yes |
Query Parameters
| Parameter | Type | Description | Required |
|---|---|---|---|
templateId | String (UUID) | Return only coupons issued from this template | ❌ No |
limit | Integer | Items per page. Default 15, maximum 100, -1 for unlimited | ❌ No |
nextCursor | String | Cursor from the previous page's pagination object | ❌ No |
Example Request
curl --location 'https://stage-platform-exlr8-incentives.exlr8now.com/v1/coupons?limit=50' \
--header 'X-Client-Id: YOUR_CLIENT_ID' \
--header 'X-Client-Secret: YOUR_CLIENT_SECRET'
Filter by template
curl --location 'https://stage-platform-exlr8-incentives.exlr8now.com/v1/coupons?templateId=5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9&limit=50' \
--header 'X-Client-Id: YOUR_CLIENT_ID' \
--header 'X-Client-Secret: YOUR_CLIENT_SECRET'
This is the practical way to measure one campaign, rather than paging every coupon the merchant has issued.
Response
Successful Response — 200 OK
{
"success": true,
"data": [
{
"couponId": "7c8d9e0f-1a2b-4c3d-9e4f-5a6b7c8d9e0f",
"merchantId": "b1946ac9-2c3d-4e5f-8a7b-6c5d4e3f2a1b",
"programId": "3c6e0b8a-9c15-4f2b-8d1e-7a5b9c0d2e4f",
"couponTemplateId": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
"codeHash": "8d7e6f...",
"status": "ACTIVE",
"redemptionCount": 0,
"usageLimitTotal": 1,
"expiresAt": "2026-11-23T11:30:00Z",
"version": 1,
"createdAt": "2026-08-25T11:30:00Z",
"updatedAt": "2026-08-25T11:30:00Z"
}
],
"pagination": {
"nextCursor": "eyJpZCI6IjdjOGQ5ZTBmIn0",
"hasMore": true
}
}
Each element carries the same fields as Get Coupon By ID.
Pagination Fields
| Field | Type | Description |
|---|---|---|
nextCursor | String | Opaque cursor for the next page |
hasMore | Boolean | Whether further pages exist |
Paging through results
async function listAllCoupons() {
const coupons = [];
let cursor = null;
do {
const url = new URL(`${baseUrl}/v1/coupons`);
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("nextCursor", cursor);
const res = await fetch(url, {
headers: {
"X-Client-Id": clientId,
"X-Client-Secret": clientSecret,
},
});
const body = await res.json();
coupons.push(...body.data);
cursor = body.pagination.hasMore ? body.pagination.nextCursor : null;
} while (cursor);
return coupons;
}
Treat the cursor as opaque. Results are already scoped to your merchant.
Campaign performance
redemptionCount and status across a campaign's coupons give you redemption rates without any additional endpoint:
const summary = coupons.reduce(
(acc, c) => {
acc.total += 1;
if (c.redemptionCount > 0) acc.redeemed += 1;
if (c.status === "EXPIRED") acc.expired += 1;
return acc;
},
{ total: 0, redeemed: 0, expired: 0 }
);
const redemptionRate = summary.redeemed / summary.total;
:::caution Not a real-time feed This is a snapshot for reporting, not a change stream. Coupons issued or redeemed while you are paging may not appear consistently. For an authoritative view of one coupon, fetch it directly. :::
Error Responses
| Error code | HTTP | Cause |
|---|---|---|
BAD_REQUEST | 400 | limit is not an integer, or the cursor is malformed |