# Coupons API

> Create coupons and manage their pools of unique codes for emails.

Base URL: `https://api.emailit.com/v2`. Authenticate with `Authorization: Bearer <API key>`.

## Create a coupon — POST /coupons

> Create a coupon with a pool of unique codes, or one shared code, for your emails. Alpha: open to the Emailit team only.

# Create a coupon

Create a coupon. Put it in an email with `{{coupon "key"}}`: when the email is rendered, each recipient gets a code. Requires an API key with the `full` scope.

> **Commerce is in alpha:** Commerce is open only to the Emailit team while we test it. Other requests return `403` with `error: "commerce_alpha"`. API keys work only in workspaces owned by the Emailit team. See [Who can use Commerce](/docs/commerce/#who-can-use-commerce).

- A **unique** coupon holds a pool of single-use codes. Each recipient is assigned the oldest free code, one per campaign or automation, and gets the same code again whenever that email is rendered for them. Upload your shop's codes here or with [Add coupon codes](/docs/api-reference/coupons/add-codes/), or generate codes in Emailit and import them into your shop.
- A **static** coupon has one code, `static_code`, for everyone.

When an [order](/docs/api-reference/orders/create/) lists a code in `coupon_codes`, the code is marked redeemed. See [Products, coupons and the cart](/docs/campaigns/products-and-coupons/#coupon-codes).

`POST /coupons`

## Request Body

- `key` (string, required): The name you use in content, unique in the workspace. 1 to 64 lowercase letters, digits, `-` or `_`, starting with a letter or digit. Uppercase letters are lowercased.

- `name` (string, required): Coupon name shown in the dashboard, up to 191 characters.

- `description` (string | null): Note for your team. Longer text is cut to 1,000 characters.

- `kind` (string): `unique` (default) or `static`.

- `static_code` (string | null): Up to 191 characters. Required for a static coupon: the code everyone gets. For a unique coupon, the text recipients get when the pool is empty or the coupon has expired. Without it, they get an empty string.

- `discount_label` (string | null): Short description of the discount, such as `10% off`. Longer text is cut to 191 characters.

- `expires_at` (string | null): ISO 8601 date-time. After it, recipients of a unique coupon get `static_code` instead of a code, also in emails rendered again. Emailit doesn't enforce the expiry in your shop.

- `codes` (string[] | string): Codes to add to the pool: an array, or text with one code per line. For CSV text, the first column is read and a header such as `code` or `coupon_code` is skipped. A code is 2 to 64 letters, digits, `-`, `_` or `.`, starting with a letter or digit. Invalid codes are left out and duplicates are added once. Up to 10,000 codes.

## Returns

`201 Created` with the [coupon object](/docs/api-reference/coupons/get/). Returns `422` when a field is invalid or another coupon already has the key.
**Request** `POST /coupons`

**cURL**

```bash
curl -X POST https://api.emailit.com/v2/coupons \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "welcome-10",
    "name": "Welcome 10% off",
    "description": "First-order discount for new subscribers.",
    "kind": "unique",
    "static_code": "WELCOME10",
    "discount_label": "10% off",
    "expires_at": "2026-12-31T23:59:59Z",
    "codes": ["WELCOME-7KQ2M9XA", "WELCOME-H4TZ8RCN", "WELCOME-P2WD6YFE"]
  }'
```

**201**

```json
{
  "object": "coupon",
  "id": "cpn_3sMKUvFbx63T9hhDoNea7fmo1fQ",
  "key": "welcome-10",
  "name": "Welcome 10% off",
  "description": "First-order discount for new subscribers.",
  "kind": "unique",
  "static_code": "WELCOME10",
  "discount_label": "10% off",
  "expires_at": "2026-12-31T23:59:59.000Z",
  "codes_total": 3,
  "codes_available": 3,
  "codes_assigned": 0,
  "codes_redeemed": 0,
  "created_at": "2026-10-01T09:00:12.304Z",
  "updated_at": "2026-10-01T09:00:12.304Z"
}
```

**422**

```json
{
  "message": "Validation failed. key: A coupon with this key already exists.",
  "errors": {
    "key": [
      "A coupon with this key already exists."
    ]
  }
}
```

---
Source: https://emailit.com/docs/api-reference/coupons/create/

## Retrieve a coupon — GET /coupons/{id}

> Retrieve a coupon by its ID or key, with counts of its available, assigned and redeemed codes. Alpha: open to the Emailit team only.

# Retrieve a coupon

Retrieve a coupon by its ID or its key, with counts of its codes. Requires an API key with the `full` scope.

> **Commerce is in alpha:** Commerce is open only to the Emailit team while we test it. Other requests return `403` with `error: "commerce_alpha"`. API keys work only in workspaces owned by the Emailit team. See [Who can use Commerce](/docs/commerce/#who-can-use-commerce).

`GET /coupons/{id}`

## Path Parameters

- `id` (string, required): The coupon ID (`cpn_...`) or the coupon's `key`.

## The coupon object

- `object` (string): Always `coupon`.

- `id` (string): Coupon ID (`cpn_...`).

- `key` (string): The name you use in email content: `{{coupon "welcome-10"}}`. See [Products, coupons and the cart](/docs/campaigns/products-and-coupons/#coupon-codes).

- `name` (string): Coupon name shown in the dashboard.

- `description` (string | null): Note for your team.

- `kind` (string): - `unique`: a pool of single-use codes. Each recipient gets their own code, one per campaign or automation, and the same code again when the email is rendered again. - `static`: one code, `static_code`, for everyone.

- `static_code` (string | null): The shared code of a static coupon. For a unique coupon, the text recipients get when no code is left or the coupon has expired. Without it, they get an empty string.

- `discount_label` (string | null): Short description of the discount, such as `10% off`.

- `expires_at` (string | null): After this time, recipients of a unique coupon get `static_code` instead of a code.

- `codes_total` (integer): Codes in the pool.

- `codes_available` (integer): Codes that can still be handed out: not assigned to a contact and not used in an order.

- `codes_assigned` (integer): Codes assigned to a contact, including the ones already redeemed.

- `codes_redeemed` (integer): Codes used in an [order](/docs/api-reference/orders/create/) (`coupon_codes`).

- `created_at, updated_at` (string): When the coupon was created and last changed, in ISO 8601 UTC.

**Request** `GET /coupons/{id}`

**cURL**

```bash
curl https://api.emailit.com/v2/coupons/welcome-10 \
  -H "Authorization: Bearer your_api_key"
```

**200**

```json
{
  "object": "coupon",
  "id": "cpn_3sMKUvFbx63T9hhDoNea7fmo1fQ",
  "key": "welcome-10",
  "name": "Welcome 10% off",
  "description": "First-order discount for new subscribers.",
  "kind": "unique",
  "static_code": "WELCOME10",
  "discount_label": "10% off",
  "expires_at": "2026-12-31T23:59:59.000Z",
  "codes_total": 5000,
  "codes_available": 3812,
  "codes_assigned": 1188,
  "codes_redeemed": 143,
  "created_at": "2026-10-01T09:00:12.304Z",
  "updated_at": "2026-10-01T09:00:12.304Z"
}
```

**404**

```json
{
  "error": "Coupon not found",
  "message": "Coupon not found."
}
```

---
Source: https://emailit.com/docs/api-reference/coupons/get/

## Update a coupon — POST /coupons/{id}

> Change a coupon's key, name, kind, fallback or static code, label or expiry. Alpha: open to the Emailit team only.

# Update a coupon

Update a coupon. Only the fields you send change. To add codes, use [Add coupon codes](/docs/api-reference/coupons/add-codes/): `codes` in this request is ignored. Requires an API key with the `full` scope.

> **Commerce is in alpha:** Commerce is open only to the Emailit team while we test it. Other requests return `403` with `error: "commerce_alpha"`. API keys work only in workspaces owned by the Emailit team. See [Who can use Commerce](/docs/commerce/#who-can-use-commerce).

If you change the `key`, update the `{{coupon "key"}}` tags in your emails too: Emailit looks coupons up by the key in the content.

`POST /coupons/{id}`

## Path Parameters

- `id` (string, required): The coupon ID (`cpn_...`) or the coupon's current `key`.

## Request Body

Any of `key`, `name`, `description`, `kind`, `static_code`, `discount_label` and `expires_at`, with the rules of [Create a coupon](/docs/api-reference/coupons/create/). Send `null` for `description`, `static_code`, `discount_label` or `expires_at` to clear it. A coupon that is or becomes `static` must keep a `static_code`.

## Returns

`200 OK` with the updated [coupon object](/docs/api-reference/coupons/get/). Returns `404` if no coupon matches `id` and `422` when a field is invalid or the new key is taken.
**Request** `POST /coupons/{id}`

**cURL**

```bash
curl -X POST https://api.emailit.com/v2/coupons/welcome-10 \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "expires_at": "2027-01-31T23:59:59Z" }'
```

**200**

```json
{
  "object": "coupon",
  "id": "cpn_3sMKUvFbx63T9hhDoNea7fmo1fQ",
  "key": "welcome-10",
  "name": "Welcome 10% off",
  "description": "First-order discount for new subscribers.",
  "kind": "unique",
  "static_code": "WELCOME10",
  "discount_label": "10% off",
  "expires_at": "2027-01-31T23:59:59.000Z",
  "codes_total": 5000,
  "codes_available": 3812,
  "codes_assigned": 1188,
  "codes_redeemed": 143,
  "created_at": "2026-10-01T09:00:12.304Z",
  "updated_at": "2026-10-09T15:47:20.061Z"
}
```

**404**

```json
{
  "error": "Coupon not found",
  "message": "Coupon not found."
}
```

**422**

```json
{
  "message": "Validation failed. static_code: A static coupon needs its code.",
  "errors": {
    "static_code": [
      "A static coupon needs its code."
    ]
  }
}
```

---
Source: https://emailit.com/docs/api-reference/coupons/update/

## List coupons — GET /coupons

> List the coupons in your workspace, newest first, with counts of their codes. Alpha: open to the Emailit team only.

# List coupons

List the coupons in your workspace, newest first, with counts of their codes. Requires an API key with the `full` scope.

> **Commerce is in alpha:** Commerce is open only to the Emailit team while we test it. Other requests return `403` with `error: "commerce_alpha"`. API keys work only in workspaces owned by the Emailit team. See [Who can use Commerce](/docs/commerce/#who-can-use-commerce).

`GET /coupons`

## Query Parameters

- `page` (integer): Page number for pagination (minimum: 1, default: 1).

- `limit` (integer): Number of coupons to return (minimum: 1, maximum: 100, default: 25).

- `search` (string): Case-insensitive match on the coupon name or key.

- `match`, `order`, `direction`: see [Filtering](https://emailit.com/docs/api-reference/filtering/).

## Filters and sort

Filter and sort keys: see [Filtering](https://emailit.com/docs/api-reference/filtering/).

## Returns

A page of [coupon objects](/docs/api-reference/coupons/get/) in `data`, with `total_records`, `next_page_url` and `previous_page_url`. The page URLs carry only `page` and `limit`, so add your search and filters again when you follow them.
**Request** `GET /coupons`

**cURL**

```bash
curl "https://api.emailit.com/v2/coupons?kind.exact=unique" \
  -H "Authorization: Bearer your_api_key"
```

**200**

```json
{
  "data": [
    {
      "object": "coupon",
      "id": "cpn_3sMKUvFbx63T9hhDoNea7fmo1fQ",
      "key": "welcome-10",
      "name": "Welcome 10% off",
      "description": "First-order discount for new subscribers.",
      "kind": "unique",
      "static_code": "WELCOME10",
      "discount_label": "10% off",
      "expires_at": "2026-12-31T23:59:59.000Z",
      "codes_total": 5000,
      "codes_available": 3812,
      "codes_assigned": 1188,
      "codes_redeemed": 143,
      "created_at": "2026-10-01T09:00:12.304Z",
      "updated_at": "2026-10-01T09:00:12.304Z"
    }
  ],
  "total_records": 1,
  "next_page_url": null,
  "previous_page_url": null
}
```

---
Source: https://emailit.com/docs/api-reference/coupons/list/

## Delete a coupon — DELETE /coupons/{id}

> Delete a coupon and all of its codes. Alpha: open to the Emailit team only.

# Delete a coupon

Delete a coupon together with all of its codes, including assigned and redeemed ones. Orders keep the codes in their `coupon_codes`. Requires an API key with the `full` scope.

> **Commerce is in alpha:** Commerce is open only to the Emailit team while we test it. Other requests return `403` with `error: "commerce_alpha"`. API keys work only in workspaces owned by the Emailit team. See [Who can use Commerce](/docs/commerce/#who-can-use-commerce).

`DELETE /coupons/{id}`

## Path Parameters

- `id` (string, required): The coupon ID (`cpn_...`) or the coupon's `key`.

**Request** `DELETE /coupons/{id}`

**cURL**

```bash
curl -X DELETE https://api.emailit.com/v2/coupons/cpn_3sMKUvFbx63T9hhDoNea7fmo1fQ \
  -H "Authorization: Bearer your_api_key"
```

**200**

```json
{
  "object": "coupon",
  "id": "cpn_3sMKUvFbx63T9hhDoNea7fmo1fQ",
  "deleted": true
}
```

**404**

```json
{
  "error": "Coupon not found",
  "message": "Coupon not found."
}
```

---
Source: https://emailit.com/docs/api-reference/coupons/delete/

## List coupon codes — GET /coupons/{id}/codes

> List the codes of a coupon with who they were assigned to and whether they were redeemed, as JSON or as a CSV export. Alpha: open to the Emailit team only.

# List coupon codes

List the codes of a coupon, oldest first: the order in which they're handed out. Use `format=csv` to export them, for example to import generated codes into your shop. Requires an API key with the `full` scope.

> **Commerce is in alpha:** Commerce is open only to the Emailit team while we test it. Other requests return `403` with `error: "commerce_alpha"`. API keys work only in workspaces owned by the Emailit team. See [Who can use Commerce](/docs/commerce/#who-can-use-commerce).

`GET /coupons/{id}/codes`

## Path Parameters

- `id` (string, required): The coupon ID (`cpn_...`) or the coupon's `key`.

## Query Parameters

- `page` (integer): Page number for pagination (minimum: 1, default: 1).

- `limit` (integer): Number of codes to return (minimum: 1, maximum: 100, default: 50).

- `status` (string): - `available`: not assigned to a contact and not used in an order, so it can still be handed out. - `assigned`: assigned to a contact and not redeemed. - `redeemed`: used in an order.

- `format` (string): `json` (default) or `csv`. CSV returns every code that matches `status` in one file, without paging, with the columns `code`, `status`, `email`, `assigned_at` and `redeemed_at`. `key.condition` filters and sorting don't apply to the CSV.

- `match`, `order`, `direction`: see [Filtering](https://emailit.com/docs/api-reference/filtering/).

## Filters and sort

Filter and sort keys: see [Filtering](https://emailit.com/docs/api-reference/filtering/).

## Returns

A page of coupon codes in `data`, with `total_records`, `next_page_url` and `previous_page_url`. Each code has:

- `object` (string): Always `coupon_code`.

- `id` (string): Code ID (`cpc_...`).

- `code` (string): The code.

- `contact_id, email` (string | null): The contact the code is assigned to, or `null`.

- `source_id` (string | null): The campaign (`cmp_...`) or automation (`aut_...`) whose email got the code.

- `assigned_at` (string | null): When the code was assigned.

- `redeemed_at, redeemed_order_id` (string | null): When the code was redeemed and by which order (`ord_...`).

- `created_at` (string): When the code was added to the pool.

**Request** `GET /coupons/{id}/codes`

**cURL**

```bash
curl "https://api.emailit.com/v2/coupons/welcome-10/codes?status=assigned&limit=2" \
  -H "Authorization: Bearer your_api_key"
```

**200**

```json
{
  "data": [
    {
      "object": "coupon_code",
      "id": "cpc_3s6oCjlRbLFujYObEAcuiZmGKqu",
      "code": "WELCOME-H4TZ8RCN",
      "contact_id": "con_3MjLVacsYA6keJucAbOs3o3XhyA",
      "email": "grace@example.com",
      "source_id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
      "assigned_at": "2026-10-08T09:00:41.773Z",
      "redeemed_at": null,
      "redeemed_order_id": null,
      "created_at": "2026-10-01T09:00:12.304Z"
    },
    {
      "object": "coupon_code",
      "id": "cpc_3u9TrZTopqOgaqT9FcZFareUFGj",
      "code": "WELCOME-P2WD6YFE",
      "contact_id": "con_3EwrXnu9OaQWlfI1AyPUUEoYjQs",
      "email": "ada@example.com",
      "source_id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
      "assigned_at": "2026-10-08T09:00:41.802Z",
      "redeemed_at": null,
      "redeemed_order_id": null,
      "created_at": "2026-10-01T09:00:12.304Z"
    }
  ],
  "total_records": 1045,
  "next_page_url": "/v2/coupons/cpn_3sMKUvFbx63T9hhDoNea7fmo1fQ/codes?page=2&limit=2",
  "previous_page_url": null
}
```

**CSV**

```text
code,status,email,assigned_at,redeemed_at
WELCOME-7KQ2M9XA,redeemed,ada@example.com,2026-10-08T09:00:41.751Z,2026-10-08T14:32:10.000Z
WELCOME-H4TZ8RCN,assigned,grace@example.com,2026-10-08T09:00:41.773Z,
WELCOME-QX3B9NMV,available,,,
```

**404**

```json
{
  "error": "Coupon not found",
  "message": "Coupon not found."
}
```

---
Source: https://emailit.com/docs/api-reference/coupons/list-codes/

## Add coupon codes — POST /coupons/{id}/codes

> Upload your shop's codes to a unique coupon, or generate random codes in Emailit. Alpha: open to the Emailit team only.

# Add coupon codes

Add codes to the pool of a unique coupon. Either upload codes that exist in your shop with `codes`, or let Emailit create random codes with `generate` and then import them into your shop with [List coupon codes](/docs/api-reference/coupons/list-codes/) (`format=csv`). Codes already in the pool are skipped. Static coupons have one code and return `422`. Requires an API key with the `full` scope.

> **Commerce is in alpha:** Commerce is open only to the Emailit team while we test it. Other requests return `403` with `error: "commerce_alpha"`. API keys work only in workspaces owned by the Emailit team. See [Who can use Commerce](/docs/commerce/#who-can-use-commerce).

`POST /coupons/{id}/codes`

## Path Parameters

- `id` (string, required): The coupon ID (`cpn_...`) or the coupon's `key`.

## Request Body

Send `codes` or `generate`. When you send both, `generate` is used.

- `codes` (string[] | string): Up to 10,000 codes: an array, or text with one code per line. For CSV text, the first column is read and a header such as `code` or `coupon_code` is skipped. A code is 2 to 64 letters, digits, `-`, `_` or `.`, starting with a letter or digit. Codes that don't match are returned in `invalid`.

- `generate` (object): Create random codes from uppercase letters and digits, without `0`, `O`, `1` and `I`, which are easy to mix up. - `count` (integer, required): 1 to 10,000 codes. - `prefix` (string): up to 20 letters, digits, `-` or `_`, put in front of every code. For example `WELCOME-`. - `length` (integer): random characters after the prefix, from 4 to 32. Defaults to 8. Values outside the range are clamped.

## Returns

- `added` (integer): Codes added to the pool.

- `skipped` (integer): Codes that were already in the pool.

- `invalid` (string[]): Uploaded values that aren't valid codes and weren't added.

- `coupon` (object): The [coupon object](/docs/api-reference/coupons/get/) with the new counts.

Returns `404` if no coupon matches `id`, and `422` for a static coupon, when no valid code was sent, when more than 10,000 codes were sent, or when `generate.count` or `generate.prefix` is out of range.
**Request** `POST /coupons/{id}/codes`

**cURL**

```bash
curl -X POST https://api.emailit.com/v2/coupons/welcome-10/codes \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "generate": { "count": 5000, "prefix": "WELCOME-", "length": 8 } }'
```

**200**

```json
{
  "added": 5000,
  "skipped": 0,
  "invalid": [],
  "coupon": {
    "object": "coupon",
    "id": "cpn_3sMKUvFbx63T9hhDoNea7fmo1fQ",
    "key": "welcome-10",
    "name": "Welcome 10% off",
    "description": "First-order discount for new subscribers.",
    "kind": "unique",
    "static_code": "WELCOME10",
    "discount_label": "10% off",
    "expires_at": "2026-12-31T23:59:59.000Z",
    "codes_total": 5003,
    "codes_available": 5003,
    "codes_assigned": 0,
    "codes_redeemed": 0,
    "created_at": "2026-10-01T09:00:12.304Z",
    "updated_at": "2026-10-01T09:00:12.304Z"
  }
}
```

**422**

```json
{
  "message": "Validation failed. codes: Static coupons have one code; set static_code instead.",
  "errors": {
    "codes": [
      "Static coupons have one code; set static_code instead."
    ]
  }
}
```

---
Source: https://emailit.com/docs/api-reference/coupons/add-codes/
