> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.brevo.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.brevo.com/_mcp/server.

# Set up a loyalty program

## Overview

This guide walks through creating and configuring a loyalty program via API — from creation through to publishing. You can also configure programs in the Brevo UI (**Loyalty > Programs**) and use the API exclusively for runtime operations.

---

## Authentication

All Loyalty API requests require your Brevo API key as a request header.

```bash
-H "api-key: YOUR_API_KEY"
-H "Content-Type: application/json"
```

Retrieve your API key from **Settings > API Keys** in the Brevo app, or at [app.brevo.com/settings/keys/api](https://app.brevo.com/settings/keys/api).

> **Warning**
>
> Never expose your API key in client-side code. All Loyalty API calls must be made server-side.

---

## Steps

### Create the program

**Endpoint:** `POST https://api.brevo.com/v3/loyalty/config/programs`

```bash
curl --request POST \
  --url https://api.brevo.com/v3/loyalty/config/programs \
  --header 'api-key: YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --data '{
    "name": "VIP Club",
    "description": "Earn points on every purchase and unlock exclusive rewards."
  }'
```

**Request parameters**

| Parameter     | Type   | Required | Description                                     |
| ------------- | ------ | -------- | ----------------------------------------------- |
| `name`        | string | Yes      | Program name, max 128 characters                |
| `description` | string | No       | Customer-facing description, max 256 characters |
| `documentId`  | string | No       | Optional external document reference            |
| `meta`        | object | No       | Arbitrary key-value metadata                    |

**Response (200)**

```json
{
  "id": "27xxdd7a-af67-0020-ba65-19d60000a26e",
  "name": "VIP Club",
  "description": "Earn points on every purchase and unlock exclusive rewards.",
  "state": "inactive",
  "createdAt": "2025-03-01T10:00:00.000Z",
  "updatedAt": "2025-03-01T10:00:00.000Z"
}
```

Save the returned `id` — you will use it as `{pid}` (program ID) in all subsequent calls.

> **Note**
>
> `state` starts as `inactive`. Members cannot be enrolled until the program is published in Step 4.

### Define a balance definition

**Endpoint:** `POST https://api.brevo.com/v3/loyalty/balance/programs/{pid}/balance-definitions`

```bash
curl --request POST \
  --url https://api.brevo.com/v3/loyalty/balance/programs/27xxdd7a-.../balance-definitions \
  --header 'api-key: YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --data '{
    "name": "Purchase Points",
    "unit": "POINTS",
    "balanceAvailabilityDurationValue": 12,
    "balanceAvailabilityDurationUnit": "month",
    "balanceOptionCreditRounding": "natural",
    "balanceOptionDebitRounding": "natural"
  }'
```

**Request parameters**

| Parameter                                                              | Type         | Required | Description                                                                                                                                                                                                                             |
| ---------------------------------------------------------------------- | ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                                                                 | string       | Yes      | Internal name for this balance type, max 128 characters                                                                                                                                                                                 |
| `unit`                                                                 | string       | Yes      | One of a fixed set of unit codes: `POINTS`, `EUR`, `USD`, `MXN`, `GBP`, `INR`, `CAD`, `SGD`, `RON`, `JPY`, `MYR`, `CLP`, `PEN`, `MAD`, `AUD`, `CHF`, `BRL`                                                                              |
| `description`                                                          | string       | No       | Short description, max 256 characters                                                                                                                                                                                                   |
| `balanceAvailabilityDurationValue` + `balanceAvailabilityDurationUnit` | int + string | No       | Duration before the balance expires (unit: `day`, `week`, `month`, `year`)                                                                                                                                                              |
| `balanceAvailabilityDurationModifier`                                  | string       | No       | `noModification`, `startOfPeriod`, or `endOfPeriod` — when within the duration the balance actually expires                                                                                                                             |
| `balanceExpirationDate`                                                | string       | No       | Fixed expiration date (`dd/mm` format), as an alternative to duration-based expiry                                                                                                                                                      |
| `balanceOptionCreditRounding` / `balanceOptionDebitRounding`           | string       | No       | `lower`, `upper`, or `natural` — rounding strategy for credit/debit transactions                                                                                                                                                        |
| `balanceOptionAmountOvertakingStrategy`                                | string       | No       | `strict` or `partial` — whether partial credit is allowed when a transaction would exceed `maxAmount`                                                                                                                                   |
| `maxAmount` / `minAmount`                                              | number       | No       | Maximum/minimum allowable balance                                                                                                                                                                                                       |
| `maxCreditAmountLimit` / `maxDebitAmountLimit`                         | number       | No       | Maximum amount that can be credited/debited in a single operation                                                                                                                                                                       |
| `imageRef`                                                             | string       | No       | URL of an optional image reference                                                                                                                                                                                                      |
| `meta`                                                                 | object       | No       | Additional metadata for the balance definition. Supports `isInternal` (boolean) to mark the definition as internal. Internal balance definitions are excluded from member-facing balance reads unless `includeInternal=true` is passed. |

### Configure tier groups (optional)

Creating tiers is a two-step process: first create the tier **group**, then create each **tier** within it — tiers can't be created inline with the group.

**Step 1 — create the tier group. Endpoint:** `POST https://api.brevo.com/v3/loyalty/tier/programs/{pid}/tier-groups`

```bash
curl --request POST \
  --url https://api.brevo.com/v3/loyalty/tier/programs/27xxdd7a-.../tier-groups \
  --header 'api-key: YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --data '{
    "name": "VIP Levels",
    "upgradeStrategy": "real_time",
    "downgradeStrategy": "membership_anniversary"
  }'
```

**Tier group parameters**

| Parameter                               | Type            | Required                                              | Description                                                                      |
| --------------------------------------- | --------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------- |
| `name`                                  | string          | Yes                                                   | Name of the tier group                                                           |
| `upgradeStrategy` / `downgradeStrategy` | string          | No (default `real_time`)                              | `real_time`, `membership_anniversary`, or `tier_anniversary`                     |
| `upgradeSchedule` / `downgradeSchedule` | object          | Required when the matching strategy is schedule-based | `{durationValue, durationUnit, durationModifier}` or `{scheduledDate}` (`DD/MM`) |
| `tierOrder`                             | array of string | No                                                    | Tier IDs in ascending order — set once tiers exist                               |
| `meta`                                  | object          | No                                                    | Additional metadata. Supports `isInternal` (boolean).                            |

**Tier evaluation strategy options**

| Value                    | Description                                        | Recommended use                         |
| ------------------------ | -------------------------------------------------- | --------------------------------------- |
| `real_time`              | Tier re-evaluated on every completed transaction   | Upgrades — reward immediately           |
| `membership_anniversary` | Re-evaluated once a year on enrollment anniversary | Downgrades — avoid mid-year status loss |
| `tier_anniversary`       | Re-evaluated once a year on tier entry anniversary | When tier-year tracking matters         |

> **Tip**
>
> Set `upgradeStrategy: real_time` and `downgradeStrategy: membership_anniversary`. This upgrades members instantly while protecting them from losing status mid-year.

**Step 2 — create each tier. Endpoint:** `POST https://api.brevo.com/v3/loyalty/tier/programs/{pid}/tier-groups/{gid}/tiers`

```bash
curl --request POST \
  --url https://api.brevo.com/v3/loyalty/tier/programs/27xxdd7a-.../tier-groups/2exxx6ee-.../tiers \
  --header 'api-key: YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --data '{
    "name": "Silver",
    "accessConditions": [
      { "balanceDefinitionId": "a74cxx1d-4a96-4xx3-804e-dc3xxd9axxeb", "minimumValue": 300 }
    ]
  }'
```

**Tier parameters**

| Parameter          | Type   | Required | Description                                                                                                   |
| ------------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------- |
| `name`             | string | Yes      | Name of the tier                                                                                              |
| `accessConditions` | array  | Yes      | Minimum balance thresholds required to access this tier. Each entry is `{balanceDefinitionId, minimumValue}`. |
| `imageRef`         | string | No       | Image URL for the tier                                                                                        |
| `tierRewards`      | array  | No       | Rewards granted on reaching this tier, as `[{rewardId}]`                                                      |

> **Warning**
>
> Access-condition thresholds within the same group must be unique — two tiers cannot share the same entry point. Any change requires re-publishing the program.

### Publish the program

**Endpoint:** `POST https://api.brevo.com/v3/loyalty/config/programs/{pid}/publish`

```bash
curl --request POST \
  --url https://api.brevo.com/v3/loyalty/config/programs/27xxdd7a-.../publish \
  --header 'api-key: YOUR_API_KEY'
```

**Response (200):** Returns the updated program object with `"state": "active"`.

> **Warning**
>
> Any configuration changes made after publishing — to tiers, balance definitions, or rewards — require calling `/publish` again before they take effect for members.