Coupons
Discount codes a shopper types in at checkout.
Codes a shopper types in. See Coupons.
List coupons
/api/v1/kahunacart/coupons
Discount codes with their type, percentage or amount, minimum and maximum cart totals, schedule, usage limits, how many times each has been redeemed, and the restriction matrix: what each is scoped to, whether it pays the postage, whether it skips items already on sale, how many units it covers, which addresses it is reserved for, and its conditions as a nested conditions document (an empty all when it has none). A coupon is what a shopper types in at the till; a site-wide automatic discount is a sale, listed by list_sales.
Parameters
| Name | Type | Description |
|---|---|---|
| page optional | integer |
At least 1.
Default:
1 |
| per_page optional | integer |
At least 1.
Default:
100 |
| group_id optional | string | A campaign id to list only its codes, or "none" for codes in no campaign; applied before the sort and the page, and total counts the same set |
| id optional | integer | Only this code, for opening one from a link |
| sort optional | string |
A column to sort every row by before it is paged; leave out for the usual order
One of:
code, discount, expires, usage, status |
| dir optional | string |
One of:
asc, descDefault:
asc |
| q optional | string | Words to search for in the code and the addresses a code is kept to; every word has to be there, in any order. Applied before the sort and the page, and total counts what it found |
Create a coupon
/api/v1/kahunacart/coupons
Create a discount code. The code is required and must be unique after normalising, so a duplicate answers 409. Use percent with type percent and amount with the two fixed types. Every optional numeric field is genuinely nullable: leaving the minimum total empty means there is no minimum, which a stored zero would not say.
Parameters
| Name | Type | Description |
|---|---|---|
| code required | string | What the shopper types; lowercased and trimmed, and unique |
| type optional | string |
percent takes a share off, fixed_cart takes an amount off the cart total, fixed_product takes an amount off each covered line
One of:
percent, fixed_cart, fixed_product |
| percent optional | string or null | Percentage as a decimal string, for example "10" |
| amount optional | string or null | Decimal amount in the store currency for the two fixed types |
| min_total optional | string or null | Smallest cart total this code works on |
| max_total optional | string or null | Largest cart total this code works on |
| currency_amounts optional | object | The amounts in the store's other currencies, only on a store that sells in more than one: currency code to {amount, min_total, max_total} as decimal strings in that currency's own decimal places, for example {"EUR": {"amount": "4.50", "min_total": "20.00"}}. A code with no amount in the order's currency does not apply there: a fixed coupon needs an amount, and any coupon with a minimum or maximum total in the store currency needs one in the other currency too. An empty string removes an amount and a key left out is left alone. A currency the store does not sell in or an amount that is not an amount in its currency is refused with a 422 and nothing is written. |
| starts_at optional | integer or null | Unix timestamp; null for no start date |
| expires_at optional | integer or null | Unix timestamp; null for no expiry |
| usage_limit optional | integer or null | Total redemptions allowed; null for unlimited |
| usage_limit_per_user optional | integer or null | |
| enabled optional | boolean | |
| individual_use optional | boolean | This code cannot be combined with another |
| basis optional | string |
Which price a percentage comes off. `current` is what the line is worth now, sale included, and is the default; `list` is the catalogue price, so the code stacks on top of a sale. Ignored by the two fixed types, which are not measured against a price. A line never gives back more than the customer is paying for it either way.
One of:
current, list |
| product_ids optional | array of integer | Narrows the code to these products; an empty list is the whole catalog |
| scopes optional | array of object | What this code is limited to. An entry is a product, a variant, a category and everything under it, or a tag, and each one either includes or excludes. No entries is the whole catalog; entries that only exclude are the whole catalog minus what they name; an exclusion always beats an inclusion. Sending this replaces the whole set, empty list included. Keys: `kind`, `ref_id`, `exclude` |
| free_shipping optional | boolean | Zeroes the postage the order was quoted, as a negative shipping line so tax on shipping comes out right. Does not touch the tax on the goods. |
| exclude_sale_items optional | boolean | A line a sale has already discounted is not eligible for this code |
| limit_to_items optional | integer or null | The discount covers at most this many units, taken cheapest first. Null for no limit. |
| allowed_emails optional | array of string | Addresses this code is reserved for. An entry is an address or a `*@domain` pattern, matched case-insensitively against the checkout email. An empty list is a code anybody may use. |
| conditions optional | object | Who the code is for and what the cart has to hold, as the nested document a sale's conditions take: {"container": "all", "children": [{"attribute": "customer_group", "operator": "eq", "value": "trade"}]}. Containers are all, any or none and they nest; a bare list is read as an `all`, and null or an empty container removes every condition. Attributes come from list_rule_attributes, and an unknown attribute or operator is refused with a 422 and nothing is written. A code that fails a condition is refused with a sentence naming it. What is in the cart is judged on the cart page, and so is the customer for a signed-in shopper; `order_country`, and the customer for a guest, wait until checkout. A money condition is in the store currency unless it names another, as on create_sale. |
| grants_customer_group_id optional | integer or null | A customer group the buyer is put into when this code is redeemed. The grant happens as the order completes, so a code on a basket that never pays grants nothing; the group is added to any the customer is already in, and none is taken away; and refunding the order gives the code back but leaves the group. Null for a code that grants nothing, which is every code unless it says otherwise. Not `group_id` — that names the campaign a generated code was minted into. |
| utm_campaign optional | string or null | An ad campaign tag. A visitor who lands on any page with `?utm_campaign=` set to this value holds the code exactly as a `?coupon=` link would, so an ad already running gets the discount without editing its URL. Matched ignoring case, stored lowercased, at most 100 characters; null or empty clears it. A code the visitor already holds or has on the cart is left alone. |
Update a coupon
/api/v1/kahunacart/coupons/{id}
Change a coupon's fields. Only what is sent is written, and changing the code to one another coupon already holds answers 409.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Coupon id |
| code optional | string | |
| type optional | string |
One of:
percent, fixed_cart, fixed_product |
| percent optional | string or null | |
| amount optional | string or null | |
| min_total optional | string or null | |
| max_total optional | string or null | |
| currency_amounts optional | object | The amounts in the store's other currencies; see create_coupon |
| starts_at optional | integer or null | |
| expires_at optional | integer or null | |
| usage_limit optional | integer or null | |
| usage_limit_per_user optional | integer or null | |
| enabled optional | boolean | |
| individual_use optional | boolean | |
| basis optional | string |
Which price a percentage comes off; see create_coupon
One of:
current, list |
| product_ids optional | array of integer | |
| scopes optional | array of object | What this code is limited to. An entry is a product, a variant, a category and everything under it, or a tag, and each one either includes or excludes. No entries is the whole catalog; entries that only exclude are the whole catalog minus what they name; an exclusion always beats an inclusion. Sending this replaces the whole set, empty list included. Keys: `kind`, `ref_id`, `exclude` |
| free_shipping optional | boolean | Zeroes the postage the order was quoted, as a negative shipping line so tax on shipping comes out right. Does not touch the tax on the goods. |
| exclude_sale_items optional | boolean | A line a sale has already discounted is not eligible for this code |
| limit_to_items optional | integer or null | The discount covers at most this many units, taken cheapest first. Null for no limit. |
| allowed_emails optional | array of string | Addresses this code is reserved for. An entry is an address or a `*@domain` pattern, matched case-insensitively against the checkout email. An empty list is a code anybody may use. |
| conditions optional | object | The code's conditions as a nested document; see create_coupon. Left out leaves them as they are, null or an empty container removes them, and anything else replaces them. |
| grants_customer_group_id optional | integer or null | A customer group the buyer is put into when this code is redeemed. The grant happens as the order completes, so a code on a basket that never pays grants nothing; the group is added to any the customer is already in, and none is taken away; and refunding the order gives the code back but leaves the group. Null for a code that grants nothing, which is every code unless it says otherwise. Not `group_id` — that names the campaign a generated code was minted into. |
| utm_campaign optional | string or null | An ad campaign tag. A visitor who lands on any page with `?utm_campaign=` set to this value holds the code exactly as a `?coupon=` link would, so an ad already running gets the discount without editing its URL. Matched ignoring case, stored lowercased, at most 100 characters; null or empty clears it. A code the visitor already holds or has on the cart is left alone. |
Delete a coupon
/api/v1/kahunacart/coupons/{id}
Delete a discount code. The redemption log is left alone, because it is the audit trail behind orders that have already been placed and those orders outlive the coupon.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | Coupon id |
Fields are code, type (percent, fixed_cart or fixed_product), percent, amount, basis (current or list, read on percent only), min_total, max_total, starts_at, expires_at, usage_limit, usage_limit_per_user, enabled, individual_use and product_ids. Every optional numeric field is nullable, so an empty value clears the column rather than writing a zero.