Issues personalised unique single-use coupon codes at scale.
The Email Code API issues unique single-use coupon codes for a given promotion on demand. It is designed for platforms that send personalised messages to large numbers of customers at once — such as Customer Engagement Platforms (CEPs), CRM systems, SMS, and push notifications — where each recipient needs their own unique code at send time.
Each request identifies a recipient via a linkedUniqueReference (typically their email address or customer ID). If a code has already been issued to that recipient for the same promotion, the existing code is returned rather than generating a new one, making repeated calls safe.
Endpoint
POST https://email-code.uniqodo.com/api/code
Authentication
All requests must include a valid API key in the X-API-Key header.
X-API-Key: your-api-key
API keys are scoped to an account type:
| Type | Access rule |
|---|---|
| Merchant | Can issue codes for promotions owned by their merchant account |
| Distributor | Can issue codes for promotions assigned to their distributor account |
Request
Headers
| Header | Required | Value |
|---|---|---|
Content-Type | Yes | application/json |
X-API-Key | Yes | Your API key |
Body
| Field | Type | Required | Description |
|---|---|---|---|
promotionId | string | Yes | UUID of the promotion |
linkedUniqueReference | string | Yes | Unique identifier for the recipient (typically their email address) |
locale | string | No | Locale of the request (e.g. en). Accepted but not currently used |
expiresAt | string (ISO-8601) | No | Override the code expiry date. If omitted, expiry is determined by the promotion's settings |
countdownStartsAt | string (ISO-8601) | No | Start point for countdown-based expiry. Used when the promotion has a relative expiry window (e.g. "valid for 7 days") |
Example request
curl -X POST https://email-code.uniqodo.com/api/code \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"locale": "en",
"promotionId": "d939a3683ce2c4f5ca51015504cce370",
"linkedUniqueReference": "[email protected]",
"expiresAt": "2026-06-15T00:00:00+00:00",
"countdownStartsAt": "2026-05-15T00:00:00+00:00"
}'Response
Success — 200 OK
200 OK{
"codeClaim": {
"code": "ABC123",
"createdAt": "2026-05-15T10:00:00+00:00",
"endDate": "2026-06-15T00:00:00+00:00",
"promotionId": "d939a3683ce2c4f5ca51015504cce370",
"destinationUrl": "https://example.com/shop?code={code}",
"userId": "a1b2c3d4e5f6..."
},
"errors": []
}| Field | Description |
|---|---|
code | The issued promo code |
createdAt | When the code was issued (UTC, ISO-8601) |
endDate | When the code expires (UTC, ISO-8601) |
promotionId | UUID of the promotion |
destinationUrl | Landing page URL for the promotion. May be null if not configured |
userId | Hashed representation of linkedUniqueReference |
Idempotency: if the same
linkedUniqueReferencerequests a code for the same promotion again and their per-user limit has not been exceeded, the existing code is returned rather than a new one being issued.
Error responses
All errors follow the same envelope:
{
"codeClaim": null,
"errors": [
{
"code": <http-status>,
"message": "<description>"
}
]
}| HTTP status | Cause |
|---|---|
400 Bad Request | Missing or invalid request body fields (see messages below) |
401 Unauthorized | X-API-Key header is missing, or the key is invalid/inactive |
403 Forbidden | API key is valid but not authorised for the requested promotion |
404 Not Found | No promotion found for the given promotionId UUID |
422 Unprocessable Entity | Code could not be issued — e.g. the promotion's third-party code list is exhausted |
500 Internal Server Error | Unexpected server error |
400 error messages
| Message | Cause |
|---|---|
Request body must be valid JSON. | The Body is missing or not valid JSON |
promotionId is required. | promotionId field is absent or empty |
linkedUniqueReference is required. | linkedUniqueReference field is absent or empty |
expiresAt must be a valid ISO-8601 date string. | expiresAt value cannot be parsed as a date |
countdownStartsAt must be a valid ISO-8601 date string. | countdownStartsAt value cannot be parsed as a date |
Notes
expiresAtandcountdownStartsAtare mutually usable but serve different purposes. UseexpiresAtto set an absolute expiry; usecountdownStartsAtwith promotions configured for relative expiry (e.g. "code valid for 7 days from email send date"). If both are provided,expiresAttakes precedence.userIdin the response is a hashed value by default unless disabled on your account, not the rawlinkedUniqueReference.- A cookie (
cl_<promotionId>) is set on the response for browser-based flows. This can be safely ignored for server-to-server integrations.
