Set up a loyalty program

Learn how to create, configure, and publish a loyalty program via the Brevo API.

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.

$-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.

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


Steps

1

Create the program

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

$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

ParameterTypeRequiredDescription
namestringYesProgram name, max 128 characters
descriptionstringNoCustomer-facing description, max 256 characters
documentIdstringNoOptional external document reference
metaobjectNoArbitrary key-value metadata

Response (200)

1{
2 "id": "27xxdd7a-af67-0020-ba65-19d60000a26e",
3 "name": "VIP Club",
4 "description": "Earn points on every purchase and unlock exclusive rewards.",
5 "state": "inactive",
6 "createdAt": "2025-03-01T10:00:00.000Z",
7 "updatedAt": "2025-03-01T10:00:00.000Z"
8}

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

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

2

Define a balance definition

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

$curl --request POST \
> --url https://api.brevo.com/v3/loyalty/config/programs/27xxdd7a-.../balance-definitions \
> --header 'api-key: YOUR_API_KEY' \
> --header 'content-type: application/json' \
> --data '{
> "name": "Purchase Points",
> "unit": "points",
> "expiryPolicy": {
> "type": "rolling",
> "duration": 12,
> "unit": "month"
> },
> "roundingPolicy": "round_half_up"
> }'

Request parameters

ParameterTypeRequiredDescription
namestringYesInternal name for this balance type
unitstringYesDisplay label shown to members (e.g. "points", "€")
expiryPolicy.typestringNonever, rolling (from last activity), fixed (calendar date each year), or inactivity-based (after N months without purchase)
expiryPolicy.duration + unitint + stringFor rolling / inactivityDuration before expiry (e.g. 12, "month")
roundingPolicystringNonone (maintain decimals), round_half_up (nearest), floor (always round down), or ceiling (always round up)
maxBalancenumberNoMaximum balance cap. Credits are refused once this limit is reached — triggers a balance_transaction_unauthorized event.
minBalancenumberNoMinimum balance threshold. Falling below this value can trigger automations.
maxCreditPerOperationnumberNoMaximum amount that can be credited in a single transaction.
maxDebitPerOperationnumberNoMaximum amount that can be debited in a single transaction.
creditFrequencyLimitobjectNoMaximum number of credit operations allowed per period.
debitFrequencyLimitobjectNoMaximum number of debit operations allowed per period.
metaobjectNoAdditional 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.
3

Configure tier groups (optional)

Endpoint: POST https://api.brevo.com/v3/loyalty/config/programs/{pid}/tier-groups

$curl --request POST \
> --url https://api.brevo.com/v3/loyalty/config/programs/27xxdd7a-.../tier-groups \
> --header 'api-key: YOUR_API_KEY' \
> --header 'content-type: application/json' \
> --data '{
> "name": "VIP Levels",
> "balanceDefinitionId": "{balanceDefinitionId}",
> "upgradeEvaluationTrigger": "real_time",
> "downgradeEvaluationTrigger": "membership_anniversary",
> "tiers": [
> { "name": "Bronze", "threshold": 0 },
> { "name": "Silver", "threshold": 300 },
> { "name": "Gold", "threshold": 500 }
> ]
> }'

Tier group parameters

ParameterTypeRequiredDescription
namestringYesInternal name for the tier group
balanceDefinitionIdstring (UUID)YesThe balance definition this tier group is evaluated against
upgradeEvaluationTriggerstringYesWhen a member is evaluated for a higher tier
downgradeEvaluationTriggerstringYesWhen a member is evaluated for a lower tier
tiersarrayYesOrdered list of tiers. Each tier requires name and threshold.
metaobjectNoAdditional metadata for the tier group. Supports isInternal (boolean) to mark the group as internal.

Tier evaluation trigger options

ValueDescriptionRecommended use
real_timeTier re-evaluated on every completed transactionUpgrades — reward immediately
membership_anniversaryRe-evaluated once a year on enrollment anniversaryDowngrades — avoid mid-year status loss
tier_anniversaryRe-evaluated once a year on tier entry anniversaryWhen tier-year tracking matters

Set upgradeEvaluationTrigger: real_time and downgradeEvaluationTrigger: membership_anniversary. This upgrades members instantly while protecting them from losing status mid-year.

Tier thresholds within the same group must be unique — two tiers cannot share the same entry point. Any threshold change requires re-publishing the program.

4

Publish the program

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

$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".

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