Coupons
Discount codes shoppers type at checkout: a percentage or a fixed amount off, with limits on who, when and how often.
Reach them through admin.coupons. Reading needs the marketing:read scope, changing them marketing:write.
List coupons
admin.coupons.list(query?: ListQuery & { code?: string }): Promise<CursorPage<AdminCoupon>>
One page of the shop's coupons, newest first, or oldest change first with updatedSince. Read the next page with meta.nextCursor as cursor. Scope: marketing:read.
Parameters
| Parameter |
Type |
|
query |
object |
The query fields below, by name. |
Query fields
| Parameter | Type | Description |
|---|
code | string | An exact code (case-insensitive). At most 255 characters. |
limit | integer | At least 1. At most 100. Default 50. |
cursor | string | meta.nextCursor (or the cursor in links.next) of the previous page. At most 1000 characters. |
updatedSince | string (date-time) | Records changed at or after this time (ISO 8601, any offset or Z), oldest change first, for incremental sync. |
Returns
A CursorPage<AdminCoupon>:
| Field | Type | Description |
|---|
links | object | |
links.first | null | |
links.last | null | |
links.prev | string (uri) or null | |
links.next | string (uri) or null | |
meta | object | |
meta.path | string (uri) | |
meta.perPage | integer | |
meta.nextCursor | string or null | |
meta.prevCursor | string or null | |
data | array of AdminCoupon | |
data[].id | integer | |
data[].code | string | |
data[].type | string | One of percentage, fixed. |
data[].value | number | |
data[].minOrderValue | number or null | |
data[].maxDiscountAmount | number or null | |
data[].usageLimit | integer or null | |
data[].usageLimitPerCustomer | integer or null | |
data[].usedCount | integer or null | |
data[].autoApply | boolean | |
data[].firstOrderOnly | boolean | |
data[].combinableWithOffers | boolean | |
data[].startsAt | string (date-time) or null | |
data[].endsAt | string (date-time) or null | |
data[].status | boolean | |
data[].createdAt | string (date-time) or null | |
data[].updatedAt | string (date-time) or null | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | admin_key_in_browser (an Origin header was sent), ip_not_allowed, plan_required(the shop's plan lacks the Integration API, or the feature the endpoint needs), orinsufficient_scope. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
const { data } = await admin.coupons.list({ code: 'WELCOME10' });
const coupon = data[0];
Iterate over every coupon
admin.coupons.all(query?: ListQuery & { code?: string }): AsyncGenerator<AdminCoupon, void, undefined>
Every coupon matching the query, one at a time, fetching the next page as you read. It takes the same query as list, without cursor. Scope: marketing:read.
Returns
An async generator of AdminCoupon, each:
| Field | Type | Description |
|---|
[].id | integer | |
[].code | string | |
[].type | string | One of percentage, fixed. |
[].value | number | |
[].minOrderValue | number or null | |
[].maxDiscountAmount | number or null | |
[].usageLimit | integer or null | |
[].usageLimitPerCustomer | integer or null | |
[].usedCount | integer or null | |
[].autoApply | boolean | |
[].firstOrderOnly | boolean | |
[].combinableWithOffers | boolean | |
[].startsAt | string (date-time) or null | |
[].endsAt | string (date-time) or null | |
[].status | boolean | |
[].createdAt | string (date-time) or null | |
[].updatedAt | string (date-time) or null | |
Example
for await (const coupon of admin.coupons.all({ updatedSince: lastSyncedAt })) {
await erp.saveCoupon(coupon);
}
Get a coupon
admin.coupons.get(id: number): Promise<AdminCoupon>
One coupon by its id. Scope: marketing:read.
Parameters
| Parameter |
Type |
|
id |
number |
The coupon's id. |
Returns
An AdminCoupon:
| Field | Type | Description |
|---|
id | integer | |
code | string | |
type | string | One of percentage, fixed. |
value | number | |
minOrderValue | number or null | |
maxDiscountAmount | number or null | |
usageLimit | integer or null | |
usageLimitPerCustomer | integer or null | |
usedCount | integer or null | |
autoApply | boolean | |
firstOrderOnly | boolean | |
combinableWithOffers | boolean | |
startsAt | string (date-time) or null | |
endsAt | string (date-time) or null | |
status | boolean | |
createdAt | string (date-time) or null | |
updatedAt | string (date-time) or null | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | admin_key_in_browser (an Origin header was sent), ip_not_allowed, plan_required(the shop's plan lacks the Integration API, or the feature the endpoint needs), orinsufficient_scope. |
404 | not_found: the shop has no such record. |
429 | Rate limited. |
Create a coupon
admin.coupons.create(input: AdminCouponInput, options?: RequestOptions): Promise<AdminCoupon>
A new coupon. The code is stored in upper case and must be unique in the shop. Scope: marketing:write.
Parameters
| Parameter |
Type |
|
input |
object |
The coupon's fields, below. |
options |
RequestOptions |
idempotencyKey, signal and headers (see retries). |
Request fields
| Field | Type | Description |
|---|
code required | string | Unique in the shop; stored in upper case. At most 50 characters. |
type required | string | One of percentage, fixed. |
value required | number | At most 100 for a percentage. At least 0. |
minOrderValue | number or null | At least 0. |
maxDiscountAmount | number or null | At least 0. |
usageLimit | integer or null | At least 1. |
usageLimitPerCustomer | integer or null | At least 1. |
autoApply | boolean or null | |
firstOrderOnly | boolean or null | |
combinableWithOffers | boolean or null | |
startsAt | string (date-time) or null | |
endsAt | string (date-time) or null | |
taxonomyFilters | object (map) or null | Limits the coupon to term ids, by taxonomy type id. Not returned in responses. |
taxonomyLabels | object (map) or null | Not returned in responses. |
status | boolean or null | |
Returns
The new AdminCoupon:
| Field | Type | Description |
|---|
id | integer | |
code | string | |
type | string | One of percentage, fixed. |
value | number | |
minOrderValue | number or null | |
maxDiscountAmount | number or null | |
usageLimit | integer or null | |
usageLimitPerCustomer | integer or null | |
usedCount | integer or null | |
autoApply | boolean | |
firstOrderOnly | boolean | |
combinableWithOffers | boolean | |
startsAt | string (date-time) or null | |
endsAt | string (date-time) or null | |
status | boolean | |
createdAt | string (date-time) or null | |
updatedAt | string (date-time) or null | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | admin_key_in_browser (an Origin header was sent), ip_not_allowed, plan_required(the shop's plan lacks the Integration API, or the feature the endpoint needs), orinsufficient_scope. |
409 | idempotency_in_progress: a request with this Idempotency-Key is still running. Retry with the same key after Retry-After seconds; the first answer is then replayed. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
const coupon = await admin.coupons.create({
code: 'WELCOME10',
type: 'percentage',
value: 10,
firstOrderOnly: true,
usageLimitPerCustomer: 1,
endsAt: '2026-12-31T23:59:59Z',
});
Update a coupon
admin.coupons.update(id: number, changes: Partial<AdminCouponInput>, options?: UpdateOptions): Promise<AdminCoupon>
Changes only the fields sent; the coupon must still pass the full rules. Setting status to false switches it off. Scope: marketing:write.
Parameters
| Parameter |
Type |
|
id |
number |
The coupon's id. |
changes |
object |
The fields to change, below. |
options |
UpdateOptions |
unmodifiedSince (the updatedAt you read; a newer coupon is refused with 409 edit_conflict) and idempotencyKey. |
Request fields
| Field | Type | Description |
|---|
code | string | Unique in the shop; stored in upper case. At most 50 characters. |
type | string | One of percentage, fixed. |
value | number | At most 100 for a percentage. At least 0. |
minOrderValue | number or null | At least 0. |
maxDiscountAmount | number or null | At least 0. |
usageLimit | integer or null | At least 1. |
usageLimitPerCustomer | integer or null | At least 1. |
autoApply | boolean or null | |
firstOrderOnly | boolean or null | |
combinableWithOffers | boolean or null | |
startsAt | string (date-time) or null | |
endsAt | string (date-time) or null | |
taxonomyFilters | object (map) or null | Limits the coupon to term ids, by taxonomy type id. Not returned in responses. |
taxonomyLabels | object (map) or null | Not returned in responses. |
status | boolean or null | |
updatedAt | string (date-time) or null | The record's updatedAt as you read it; a newer record gets 409 edit_conflict. |
Returns
The changed AdminCoupon:
| Field | Type | Description |
|---|
id | integer | |
code | string | |
type | string | One of percentage, fixed. |
value | number | |
minOrderValue | number or null | |
maxDiscountAmount | number or null | |
usageLimit | integer or null | |
usageLimitPerCustomer | integer or null | |
usedCount | integer or null | |
autoApply | boolean | |
firstOrderOnly | boolean | |
combinableWithOffers | boolean | |
startsAt | string (date-time) or null | |
endsAt | string (date-time) or null | |
status | boolean | |
createdAt | string (date-time) or null | |
updatedAt | string (date-time) or null | |
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | admin_key_in_browser (an Origin header was sent), ip_not_allowed, plan_required(the shop's plan lacks the Integration API, or the feature the endpoint needs), orinsufficient_scope. |
404 | not_found: the shop has no such record. |
409 | edit_conflict (the record changed after If-Unmodified-Since or updatedAt), or idempotency_in_progress. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |
Example
const coupon = await admin.coupons.get(5);
await admin.coupons.update(
5,
{ value: 15 },
{ unmodifiedSince: coupon.updatedAt ?? undefined },
);
Delete a coupon
admin.coupons.delete(id: number): Promise<void>
Deletes a coupon. Scope: marketing:write.
Parameters
| Parameter |
Type |
|
id |
number |
The coupon's id. |
Returns
Nothing.
Errors
| Status | When |
|---|
401 | invalid_api_key: no active Admin key in Authorization: Bearer. |
403 | admin_key_in_browser (an Origin header was sent), ip_not_allowed, plan_required(the shop's plan lacks the Integration API, or the feature the endpoint needs), orinsufficient_scope. |
404 | not_found: the shop has no such record. |
409 | idempotency_in_progress: a request with this Idempotency-Key is still running. Retry with the same key after Retry-After seconds; the first answer is then replayed. |
422 | Validation errors ({ message, errors }), or an idempotency refusal(idempotency_key_reused, idempotency_key_invalid). |
429 | Rate limited. |