Authentication
Every request to the Incentives API is authenticated with a pair of headers. There is no token exchange, no login call, and no expiry to manage.
Base URLs
The Incentives service is deployed separately from the Delivery Partner API, on its own hosts.
| Environment | Base URL |
|---|---|
| UAT (Testing) | https://stage-platform-exlr8-incentives.exlr8now.com |
| Production | https://prod-platform-exlr8-incentives.exlr8now.com |
Examples throughout these pages use the UAT host. Switch to the production host only once your integration is verified.
Credentials
| Header | Type | Description | Required |
|---|---|---|---|
X-Client-Id | String | Your client ID — the API client's user ID | ✅ Yes |
X-Client-Secret | String | Your client secret | ✅ Yes |
Content-Type | String | application/json on any request with a body | ✅ Yes |
Both values arrive by email when an administrator provisions your API client. See Setup Flow.
curl --location 'https://stage-platform-exlr8-incentives.exlr8now.com/v1/giftCards' \
--header 'X-Client-Id: YOUR_CLIENT_ID' \
--header 'X-Client-Secret: YOUR_CLIENT_SECRET'
:::caution Server-side only These credentials authorise issuing and redeeming real value. Never embed them in a mobile app, a browser bundle, or anything else a customer can read. All calls must originate from your backend. :::
What your credentials scope
Your credentials resolve to a single API client user, which belongs to exactly one merchant. That merchant is attached to every request automatically.
You never send a merchant ID, and you never send a user ID. Every listing is already filtered to your merchant, and every lookup is already scoped to it. An instrument belonging to another merchant is reported as not found rather than refused — the platform does not disclose that it exists.
Access
The MERCHANT_API_CLIENT role is confined to the operations plane. Configuration is a dashboard activity.
Available to you:
| Endpoint | Purpose |
|---|---|
POST /v1/giftCards | Issue a gift card |
POST /v1/giftCards/inquiry | Look up a card by code |
GET /v1/giftCards/{giftCardId} | Retrieve a card |
GET /v1/giftCards | List cards |
POST /v1/giftCards/transactions | Debit, refund or void a card |
GET /v1/giftCards/transactions/{transactionId} | Retrieve a transaction |
GET /v1/giftCards/transactions | List transactions |
POST /v1/coupons | Issue a coupon |
POST /v1/coupons/validate | Check a coupon without consuming it |
POST /v1/coupons/redeem | Redeem a coupon |
GET /v1/coupons/{couponId} | Retrieve a coupon |
GET /v1/coupons | List coupons |
Not available to you. Configuring a campaign, creating a template, and managing users are administrator actions performed in the dashboard. Those endpoints reject client credentials with 403 FORBIDDEN, and are not documented here — see Setup Flow for what they cover and who performs them.
:::note JWT is not interchangeable
An API client cannot authenticate with a bearer token, and a dashboard user cannot authenticate with client credentials. The two credential types are bound to different roles and are not substitutes. Presenting the wrong one returns 401 UNAUTHORIZED.
:::
Response format
Every response is a JSON envelope with a success flag.
Success
{
"success": true,
"data": {
"giftCardId": "9b2c4d6e-8f0a-4b1c-9d3e-5f7a1b2c3d4e"
}
}
Success, paginated
List endpoints return an array in data and a sibling pagination object.
{
"success": true,
"data": [],
"pagination": {
"nextCursor": "eyJpZCI6IjliMmM0ZDZlIn0",
"hasMore": true
}
}
Error
{
"success": false,
"errCode": "GC_INSUFFICIENT_BALANCE",
"message": "Insufficient gift card balance: requested 750.00, available 500.00"
}
Branch on errCode, never on message — the message is human-readable and may change. The full list is in Error Codes.
Authentication failures
| Error code | HTTP | Cause |
|---|---|---|
UNAUTHORIZED | 401 | Headers missing, or a credential type not permitted for this role |
API_KEY_INVALID | 401 | Unknown client ID, or the secret does not match |
USER_INACTIVE | 403 | The API client account has been deactivated |
FORBIDDEN | 403 | Authenticated, but this endpoint is not open to an API client |
A 401 API_KEY_INVALID after a working integration almost always means the secret was rotated. Check for a credentials email and redeploy the new value.
Pagination
All list endpoints use cursor pagination.
| Parameter | Type | Description |
|---|---|---|
limit | Integer | Items per page. Default 15, maximum 100. -1 returns everything. |
nextCursor | String | The opaque cursor from the previous page's pagination object |
curl --location 'https://stage-platform-exlr8-incentives.exlr8now.com/v1/giftCards?limit=50' \
--header 'X-Client-Id: YOUR_CLIENT_ID' \
--header 'X-Client-Secret: YOUR_CLIENT_SECRET'
Follow pagination.nextCursor until hasMore is false. Do not construct a cursor yourself; treat it as opaque.
Request conventions
- Unknown fields are rejected. A body carrying a field the endpoint does not define fails with
400 BAD_REQUEST. Typos surface immediately rather than being silently ignored. - Secrets go in bodies, not URLs. Gift card codes and coupon codes are always sent in a POST body so they never reach an access log or a browser history.
- Requests time out at 30 seconds. Set your client timeout above that, or you may abandon a request the platform still completes.
Next steps
- Quick Start — a working end-to-end sequence
- How Redemption Works — retry semantics and failure handling