Coupons
Coupons are discount codes redeemed at checkout — percent off, fixed amount, free shipping, or BOGO. They carry usage limits (total + per-customer), date windows, eligibility rules (products, customer segments), and platform sync state. Use these endpoints to create, list, validate, and redeem coupons.
Every operation below is on the public API surface and takes Authorization: Bearer brainerce_*. The 8 /v1/coupons/* operations are the stable, versioned surface for external integrators (list, create, get, update, delete, sync, publish, platform capabilities).
The merchant dashboard uses its own internal coupon routes (signed-in session: conflicts, bulk operations, validate-for-order). They are not part of the public contract and are not listed here.
The newsletter welcome offer (
/v1/newsletter-benefit/*) is on this page for the same reason it carries thecoupons:*scopes: it mints aCoupon, and the coupon machinery enforces it. It issues nothing on its own. A coupon appears when a shopper confirms their own newsletter signup, one per mailbox per store, andresendre-sends that same code rather than minting a second. To hand a code to a named customer, create a coupon.
List coupons with pagination
Lists coupons, newest first by default, 20 per page.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Query Parameters
ID of the store whose coupons are listed. The validator accepts a missing value, but the route answers 400 STORE_ID_REQUIRED without it.
1-based page number.
11 <= valueItems per page. Default 20. Maximum 100. Larger values are capped to 100.
201 <= value <= 100Text search on the coupon code.
Return only coupons with this status.
"ACTIVE" | "SCHEDULED" | "EXPIRED" | "DISABLED"Return only coupons of this discount type.
"PERCENTAGE" | "FIXED_AMOUNT"Only coupons that have a mapping to this platform code (case-insensitive). Legacy filter: coupons are no longer synced to other platforms.
Field to sort by.
"createdAt""code" | "createdAt" | "updatedAt" | "startsAt" | "endsAt" | "usageCount"Sort direction. Default desc.
"desc""asc" | "desc"curl -X GET "https://api.brainerce.com/api/v1/coupons?storeId=clx1a2b3c4d5e6f7g8h9&page=1&limit=20&search=string&status=ACTIVE&type=PERCENTAGE&platform=string&sortBy=code&sortOrder=asc"{
"data": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"code": "SUMMER20",
"title": "Welcome offer",
"description": "First order discount",
"type": "PERCENTAGE",
"value": "10.00",
"startsAt": "2019-08-24T14:15:22Z",
"endsAt": "2019-08-24T14:15:22Z",
"status": "ACTIVE",
"usageLimit": 100,
"usageLimitPerCustomer": 1,
"usageCount": 12,
"minimumOrderAmount": "200.00",
"maximumDiscount": "50.00",
"conditions": {
"loyaltySystem": true,
"customerId": "string",
"newsletterBenefit": true,
"boundEmail": "string",
"firstOrderOnly": true,
"emailSequenceStep": true,
"enrollmentId": "string",
"stepId": "string"
},
"applicableProducts": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"excludedProducts": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"applicableCategories": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"excludedCategories": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"regionIds": [
"string"
],
"combinesWithOther": true,
"needsSync": true,
"lastSyncedAt": "2019-08-24T14:15:22Z",
"channels": {},
"channelPublishes": [
{
"salesChannel": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
},
"connection": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
}
}
],
"vibeCodedPublishes": [
{
"salesChannel": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
},
"connection": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
}
}
],
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 137,
"totalPages": 7
}
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Create a new coupon
Honors Idempotency-Key header. See /docs/api/idempotency.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255Redemption code. Case-insensitive, and unique per store.
length <= 100Human-readable title shown in the dashboard list.
length <= 200Internal description (not shown to shoppers).
length <= 2000Discount type. PERCENTAGE = value is a percent (0-100); FIXED_AMOUNT = value is in store currency.
"PERCENTAGE" | "FIXED_AMOUNT"Discount amount, at most 2 decimal places. Interpretation depends on type: 25 for PERCENTAGE means 25% off (a PERCENTAGE value above 100 is refused); 25 for FIXED_AMOUNT means $25 off.
0 <= valueISO 8601 start date. Coupon is inactive before this timestamp. A bare YYYY-MM-DD means 00:00:00.000 of that day in the store time zone; a full timestamp is stored exactly as given.
ISO 8601 end date. Coupon is inactive after this timestamp. A bare YYYY-MM-DD means 23:59:59.999 of that day in the store time zone, so the last day itself is included; a full timestamp is stored exactly as given.
Initial status. Defaults to ACTIVE. Use SCHEDULED when startsAt is in the future.
"ACTIVE""ACTIVE" | "SCHEDULED" | "EXPIRED" | "DISABLED"Total redemption cap across all customers. Omit for unlimited; 0 is stored as unlimited too.
0 <= value <= 2147483647Per-customer redemption cap (requires customerId on the checkout). Omit for unlimited; 0 is stored as unlimited too.
0 <= value <= 2147483647Minimum cart subtotal to redeem.
0 <= value <= 1000000000000Cap on the discount value for PERCENTAGE coupons (e.g. "25% off, up to $50"). Ignored for FIXED_AMOUNT.
0 <= value <= 1000000000000Advanced conditions (customer segment, sales channel, …). Free-form JSON.
Restrict redemption to specific product IDs.
Restrict the coupon to specific regions (PRD §24). Empty/omitted = applies in all regions. Holds Region IDs of this store.
Exclude specific product IDs from the coupon.
Restrict redemption to specific category IDs.
Exclude specific category IDs from the coupon.
When true (default), this coupon can stack with other discount sources (other coupons, automatic discount rules). When false, it cannot stack.
trueLEGACY. Outside platform codes to push the coupon to. Brainerce no longer pushes coupons to outside platforms (Shopify and WooCommerce are retired and no installable connector carries coupons), so listing a platform only flags the coupon for sync and queues no useful job. It does NOT make the coupon redeemable anywhere. To make a coupon redeemable on one of the merchant's own sales channels, publish it to that channel with POST /v1/coupons/{id}/publish-sales-channel (an unpublished coupon is refused on a sales-channel storefront). Still accepted so existing callers keep working.
Per-channel field overrides, keyed by sales channel ID (for example vc_abc123). Useful when one channel wants a different code or title. Keys that are legacy outside-platform codes are still accepted but no connector reads them.
curl -X POST "https://api.brainerce.com/api/v1/coupons" \ -H "Idempotency-Key: string" \ -H "Content-Type: application/json" \ -d '{ "code": "SUMMER25", "type": "PERCENTAGE", "value": 25 }'{
"id": "clx1a2b3c4d5e6f7g8h9",
"code": "SUMMER20",
"title": "Welcome offer",
"description": "First order discount",
"type": "PERCENTAGE",
"value": "10.00",
"startsAt": "2019-08-24T14:15:22Z",
"endsAt": "2019-08-24T14:15:22Z",
"status": "ACTIVE",
"usageLimit": 100,
"usageLimitPerCustomer": 1,
"usageCount": 12,
"minimumOrderAmount": "200.00",
"maximumDiscount": "50.00",
"conditions": {
"loyaltySystem": true,
"customerId": "string",
"newsletterBenefit": true,
"boundEmail": "string",
"firstOrderOnly": true,
"emailSequenceStep": true,
"enrollmentId": "string",
"stepId": "string"
},
"applicableProducts": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"excludedProducts": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"applicableCategories": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"excludedCategories": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"regionIds": [
"string"
],
"combinesWithOther": true,
"needsSync": true,
"lastSyncedAt": "2019-08-24T14:15:22Z",
"channels": {},
"channelPublishes": [
{
"salesChannel": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
},
"connection": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
}
}
],
"vibeCodedPublishes": [
{
"salesChannel": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
},
"connection": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
}
}
],
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z",
"validationWarnings": [
{
"platform": "TIKTOK",
"warnings": [
"TIKTOK does not support product exclusions - will be ignored on sync"
]
}
]
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons"
}{
"statusCode": 409,
"code": "CONFLICT",
"message": "The request conflicts with the current state of the resource",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Get platform capabilities for coupon features
Deprecated. Always an empty object: coupons are not synced to other platforms, so there are no per-platform coupon capabilities to report.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
curl -X GET "https://api.brainerce.com/api/v1/coupons/platform-capabilities"{}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/platform-capabilities"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/platform-capabilities"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Get a single coupon by ID
Returns one coupon of this store.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Path Parameters
Coupon ID
curl -X GET "https://api.brainerce.com/api/v1/coupons/string"{
"id": "clx1a2b3c4d5e6f7g8h9",
"code": "SUMMER20",
"title": "Welcome offer",
"description": "First order discount",
"type": "PERCENTAGE",
"value": "10.00",
"startsAt": "2019-08-24T14:15:22Z",
"endsAt": "2019-08-24T14:15:22Z",
"status": "ACTIVE",
"usageLimit": 100,
"usageLimitPerCustomer": 1,
"usageCount": 12,
"minimumOrderAmount": "200.00",
"maximumDiscount": "50.00",
"conditions": {
"loyaltySystem": true,
"customerId": "string",
"newsletterBenefit": true,
"boundEmail": "string",
"firstOrderOnly": true,
"emailSequenceStep": true,
"enrollmentId": "string",
"stepId": "string"
},
"applicableProducts": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"excludedProducts": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"applicableCategories": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"excludedCategories": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"regionIds": [
"string"
],
"combinesWithOther": true,
"needsSync": true,
"lastSyncedAt": "2019-08-24T14:15:22Z",
"channels": {},
"channelPublishes": [
{
"salesChannel": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
},
"connection": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
}
}
],
"vibeCodedPublishes": [
{
"salesChannel": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
},
"connection": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
}
}
],
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Update a coupon
Supports Idempotency-Key header for safe retries.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Path Parameters
Coupon ID
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255New redemption code. Stored in upper case, unique per store (a code another coupon already uses answers 409). Cannot be null.
length <= 100Human-readable title shown in the dashboard list.
length <= 200Internal description (not shown to shoppers).
length <= 2000Discount type. PERCENTAGE = value is a percent (0-100); FIXED_AMOUNT = value is in store currency.
"PERCENTAGE" | "FIXED_AMOUNT"Discount amount, at most 2 decimal places. A PERCENTAGE value above 100 is refused only when this body also sends type: PERCENTAGE.
0 <= valueISO 8601 start date. Coupon is inactive before this timestamp. A bare YYYY-MM-DD means 00:00:00.000 of that day in the store time zone; a full timestamp is stored exactly as given.
ISO 8601 end date. Coupon is inactive after this timestamp. A bare YYYY-MM-DD means 23:59:59.999 of that day in the store time zone, so the last day itself is included; a full timestamp is stored exactly as given. Must be after startsAt when the body sends both (the same day is fine for bare dates).
New stored status.
"ACTIVE" | "SCHEDULED" | "EXPIRED" | "DISABLED"Total redemption cap across all customers.
0 <= value <= 2147483647Per-customer redemption cap (requires customerId on the checkout).
0 <= value <= 2147483647Minimum cart subtotal to redeem.
0 <= value <= 1000000000000Cap on the discount value for PERCENTAGE coupons (e.g. "25% off, up to $50"). Ignored for FIXED_AMOUNT.
0 <= value <= 1000000000000Advanced conditions (customer segment, sales channel, ...). Free-form JSON; replaces the stored object.
Restrict redemption to these product IDs (replaces the stored list). Every id must be a product of this store.
Restrict the coupon to these Region IDs of the store (replaces the stored list). An empty array = applies in every region.
Exclude these product IDs from the coupon (replaces the stored list).
Restrict redemption to these category IDs (replaces the stored list). Every id must be a category of this store.
Exclude these category IDs from the coupon (replaces the stored list).
Whether this coupon can stack with other discount sources (other coupons, automatic discount rules).
Legacy. Every update that changes a field sets the coupon needsSync flag unless this is false. Nothing syncs coupons to other platforms, so the flag has no effect.
Per-channel field overrides, keyed by sales channel ID (for example vc_abc123). Replaces the stored object. A key for a channel outside the caller's sales-channel scope is refused.
curl -X PATCH "https://api.brainerce.com/api/v1/coupons/string" \ -H "Idempotency-Key: string" \ -H "Content-Type: application/json" \ -d '{}'{
"id": "clx1a2b3c4d5e6f7g8h9",
"code": "SUMMER20",
"title": "Welcome offer",
"description": "First order discount",
"type": "PERCENTAGE",
"value": "10.00",
"startsAt": "2019-08-24T14:15:22Z",
"endsAt": "2019-08-24T14:15:22Z",
"status": "ACTIVE",
"usageLimit": 100,
"usageLimitPerCustomer": 1,
"usageCount": 12,
"minimumOrderAmount": "200.00",
"maximumDiscount": "50.00",
"conditions": {
"loyaltySystem": true,
"customerId": "string",
"newsletterBenefit": true,
"boundEmail": "string",
"firstOrderOnly": true,
"emailSequenceStep": true,
"enrollmentId": "string",
"stepId": "string"
},
"applicableProducts": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"excludedProducts": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"applicableCategories": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"excludedCategories": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "Summer Collection"
}
],
"regionIds": [
"string"
],
"combinesWithOther": true,
"needsSync": true,
"lastSyncedAt": "2019-08-24T14:15:22Z",
"channels": {},
"channelPublishes": [
{
"salesChannel": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
},
"connection": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
}
}
],
"vibeCodedPublishes": [
{
"salesChannel": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
},
"connection": {
"id": "clx1a2b3c4d5e6f7g8h9",
"name": "My storefront",
"connectionId": "vc_9f3a2b1c8d7e6f5a"
}
}
],
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z"
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 409,
"code": "CONFLICT",
"message": "The request conflicts with the current state of the resource",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Delete a coupon
Supports Idempotency-Key header for safe retries.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Path Parameters
Coupon ID
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255Optional platforms to remove the coupon from
curl -X DELETE "https://api.brainerce.com/api/v1/coupons/string" \ -H "Idempotency-Key: string" \ -H "Content-Type: application/json" \ -d '{}'{
"success": true,
"jobIds": [
"string"
],
"warnings": [
"string"
]
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Sync a coupon to all connected platforms
Deprecated no-op. Coupons are not synced to other platforms, so nothing is queued: the response has success: false and an empty jobIds. Returns 404 when the coupon does not exist. To limit a coupon to one of your storefronts, use POST /v1/coupons/{id}/publish-sales-channel. Supports Idempotency-Key header for safe retries.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Path Parameters
Coupon ID
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255curl -X POST "https://api.brainerce.com/api/v1/coupons/string/sync" \ -H "Idempotency-Key: string"{
"success": false,
"message": "Coupons are not synced to other platforms.",
"jobIds": [
"string"
]
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}/sync"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}/sync"
}{
"statusCode": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}/sync"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Publish a coupon to specific platforms
Deprecated no-op. Coupons are not published to other platforms, so no job is queued and jobIds is empty. The platforms array is still required but ignored. To limit a coupon to one of your storefronts, use POST /v1/coupons/{id}/publish-sales-channel. Supports Idempotency-Key header for safe retries.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Path Parameters
Coupon ID
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255Accepted and ignored: coupons are not published to other platforms.
curl -X POST "https://api.brainerce.com/api/v1/coupons/string/publish" \ -H "Idempotency-Key: string" \ -H "Content-Type: application/json" \ -d '{ "platforms": [ "string" ] }'{
"message": "Coupons are not published to other platforms.",
"jobIds": [
"string"
]
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}/publish"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}/publish"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}/publish"
}{
"statusCode": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/coupons/{id}/publish"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Get the newsletter signup benefit settings
Returns null when the store has never configured a benefit. enabled: false means configured but switched off — two different states, and a storefront should treat both as "offer nothing".
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
curl -X GET "https://api.brainerce.com/api/v1/newsletter-benefit/settings"{
"id": "clx1a2b3c4d5e6f7g8h9",
"storeId": "clx1a2b3c4d5e6f7g8h9",
"enabled": true,
"version": 3,
"discountType": "PERCENTAGE",
"discountValue": 10,
"minimumOrderAmount": 200,
"maximumDiscount": 50,
"combinesWithOther": false,
"validityDays": 30,
"eligibilityTtlHours": 48,
"firstOrderOnly": true,
"applicableProducts": [
"string"
],
"excludedProducts": [
"string"
],
"applicableCategories": [
"string"
],
"excludedCategories": [
"string"
],
"salesChannelIds": [
"string"
],
"content": {},
"createdAt": "2026-09-02T10:30:00.000Z",
"updatedAt": "2026-09-02T10:30:00.000Z"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/settings"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/settings"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Create or replace the newsletter signup benefit settings
A FULL REPLACEMENT, not a patch: every field is written, so omitting one clears it. Bumps the stored settings version.
Changing these settings never rewrites a promise already made. Signups still waiting for a confirmation click keep the terms they were shown at signup, and coupons already issued are untouched.
Supports Idempotency-Key header for safe retries.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255Turn the benefit on or off. Off stops new offers immediately; coupons already issued keep working, and signups still waiting for a confirmation click keep the terms they were shown.
Kind of coupon issued to a new subscriber. PERCENTAGE takes a percent off, FIXED_AMOUNT a fixed amount in the store currency.
"PERCENTAGE" | "FIXED_AMOUNT"Percent (1-100) for PERCENTAGE, or an amount in the store currency for FIXED_AMOUNT.
0.01 <= value <= 1000000Minimum order subtotal required to use the coupon.
0 <= valueCap on the discount a percentage benefit may produce. Ignored — and stored as null — for a fixed-amount benefit.
0 <= valueWhether the coupon may stack with other discounts.
Days the coupon stays usable, counted from issuance.
1 <= value <= 365Hours a signup stays eligible, counted from the moment the form was submitted. A confirmation click after this still subscribes the address but earns no coupon — the confirmation LINK itself never expires, so this is the only deadline in the flow.
1 <= value <= 8760Restrict the coupon to buyers with no previous order in this store. Checked against the address the coupon is bound to, including guest orders.
Product IDs the coupon applies to.
items <= 200Product IDs the coupon never applies to. Up to 200.
items <= 200Category IDs the coupon applies to. Empty means all categories. Every ID must belong to the store. Up to 200.
items <= 200Category IDs the coupon never applies to. Up to 200.
items <= 200Sales channels the issued coupon may be redeemed on — each the channel id from list_sales_channels, its vc_* connectionId, or its exact name (stored as the id). Leave empty for every enabled channel — an empty list is expanded at issuance, because a coupon with no channel rows is refused on every sales-channel storefront.
items <= 50Localized copy: { "en": { "headline": "...", "terms": "..." }, "he": { ... } }. Shown on the signup form and repeated in the email.
curl -X PUT "https://api.brainerce.com/api/v1/newsletter-benefit/settings" \ -H "Idempotency-Key: string" \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "discountType": "PERCENTAGE", "discountValue": 10, "combinesWithOther": true, "validityDays": 30, "eligibilityTtlHours": 72, "firstOrderOnly": true }'{
"id": "clx1a2b3c4d5e6f7g8h9",
"storeId": "clx1a2b3c4d5e6f7g8h9",
"enabled": true,
"version": 3,
"discountType": "PERCENTAGE",
"discountValue": 10,
"minimumOrderAmount": 200,
"maximumDiscount": 50,
"combinesWithOther": false,
"validityDays": 30,
"eligibilityTtlHours": 48,
"firstOrderOnly": true,
"applicableProducts": [
"string"
],
"excludedProducts": [
"string"
],
"applicableCategories": [
"string"
],
"excludedCategories": [
"string"
],
"salesChannelIds": [
"string"
],
"content": {},
"createdAt": "2026-09-02T10:30:00.000Z",
"updatedAt": "2026-09-02T10:30:00.000Z"
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/settings"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/settings"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/settings"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}List issued newsletter benefits
Paginated { data, meta }, newest first. redeemed is derived from the coupon having been used, so it is accurate the moment an order completes.
There is deliberately NO email filter: a lookup-by-address would turn this into a "does this person shop here" probe for any leaked key with coupons:read. Filter the page you get back.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Query Parameters
Dashboard only: the store the grants belong to (the dashboard route requires it). Ignored on /v1, where the store is always the one the API key belongs to, so do not send it there.
1-based page number.
11 <= valueItems per page. Default 20. Maximum 100.
201 <= value <= 100Only grants in this status.
"PENDING" | "ISSUING" | "ISSUED" | "EXPIRED" | "FAILED"Only signups created on or after this ISO-8601 date.
date-timeOnly signups created on or before this ISO-8601 date.
date-timecurl -X GET "https://api.brainerce.com/api/v1/newsletter-benefit/grants?storeId=string&page=1&limit=20&status=PENDING&from=2019-08-24T14%3A15%3A22Z&to=2019-08-24T14%3A15%3A22Z"{
"data": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"email": "[email protected]",
"status": "PENDING",
"source": "storefront",
"couponCode": "WELCOME-7K2QX",
"redeemed": false,
"expiresAt": "2026-10-02T10:30:00.000Z",
"eligibleUntil": "2026-09-04T10:30:00.000Z",
"confirmedAt": "2026-09-02T11:00:00.000Z",
"emailSentAt": "2026-09-02T11:00:05.000Z",
"attempts": 1,
"lastError": null,
"settingsVersion": 3,
"createdAt": "2026-09-02T10:30:00.000Z"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 137,
"totalPages": 7
}
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/grants"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/grants"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/grants"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}Re-send an issued newsletter benefit
Re-queues the SAME coupon code. It never mints a second one, so a support ticket cannot turn into two discounts. For a benefit whose issuance failed before any coupon existed, this retries the issuance and mails whatever comes out.
Rejects a signup that has not been confirmed yet, and one that lapsed before any coupon was minted: in both cases there is nothing to re-send and nothing that may be created.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Path Parameters
Grant ID from the grants list
curl -X POST "https://api.brainerce.com/api/v1/newsletter-benefit/grants/string/resend"{
"code": "WELCOME-7K2QX",
"discountType": "PERCENTAGE",
"discountValue": 10,
"expiresAt": "2026-10-02T10:30:00.000Z",
"minimumOrderAmount": 200,
"firstOrderOnly": true
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/grants/{grantId}/resend"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/grants/{grantId}/resend"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/grants/{grantId}/resend"
}{
"statusCode": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/newsletter-benefit/grants/{grantId}/resend"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}