Best practices

Integration patterns, performance tips, and use cases for the Brevo Loyalty API.

Integration best practices

Always enroll before crediting

Attempting to create a transaction for a contact who is not subscribed to the program returns a 422. Enroll the contact first, then credit points.

Use loyaltySubscriptionId as your bridge

Set loyaltySubscriptionId to your own customer ID at enrollment time. This lets you reference the member by your internal ID throughout the integration and pass it in eCommerce order identifiers — without maintaining a separate ID mapping table.

Use autoComplete: false for purchase flows, autoComplete: true for instant rewards

Purchase points should only be confirmed after the order is validated. Bonus credits (sign-up, birthday, referral) have no approval step and can be auto-completed.

Store transaction IDs

Always persist the id returned by Create transaction or Create balance order. You need it to call /complete or /cancel. If you lose it, you will need to query the transaction list to retrieve it.

Use meta to store your business context

Pass orderId, campaignId, or any relevant reference in the meta field. This makes debugging and auditing easier and keeps your transaction history self-documenting.

Never call Complete on an already-completed transaction

This returns a 422. Check the transaction status before completing: if it is not pending, skip the call.


Performance considerations

Parallelize read calls

The balance, tier, and voucher read endpoints are independent. Use concurrent requests to build your loyalty widget — do not chain them sequentially. See Read member data for an example.

Cache member data for display purposes

Balance and tier data do not change in real time on your frontend. Cache responses for 30–60 seconds to reduce API calls on high-traffic account pages.

Use webhooks instead of polling

Never poll the transaction endpoint to detect completion. Use the balance_value_updated webhook to trigger downstream actions. See Loyalty webhooks.

Set appropriate ttl on pending transactions

If your order confirmation flow takes up to 24 hours, set ttl: 1440 (minutes). This prevents orphaned pending transactions from accumulating if your system fails to call /complete or /cancel.


Security best practices

Keep API keys server-side

Never expose your api-key in browser JavaScript, mobile app binaries, or public repositories. All Loyalty API calls must originate from your backend.

Validate webhook signatures

Configure secure webhook calls with username and password authentication to verify that incoming webhooks originate from Brevo.

Scope API keys by environment

Use separate API keys for staging and production. Rotate keys immediately if you suspect exposure.

Audit meta fields

Treat meta as an internal field — never expose it raw to end users, as it may contain internal order references or pricing data.


Use cases

A fashion retailer credits 1 point per €1 spent on confirmed orders.

1. Customer places order → POST /loyalty/.../create-order (amount = order total, status: pending)
2. Order is confirmed → POST .../transactions/{tid}/complete (balance updated, tier re-evaluated)
3. Customer cancels → POST .../transactions/{tid}/cancel (no balance change)
4. Customer returns → POST .../transactions (negative amount) → /complete
5. Webhook balance_value_updated → trigger "You earned 89 points" email
6. Webhook tier_association_updated → trigger "Welcome to Gold" email + voucher

A beauty brand gives 200 points to a member who refers a friend who completes their first purchase.

1. Friend signs up and makes first purchase → your backend detects referral
2. POST .../transactions (amount: 200, autoComplete: true, meta: { referredContactId: 99999 })
3. Webhook balance_value_updated → trigger "You earned 200 referral points" push notification

A loyalty program sends a 50-point bonus 7 days before a member’s birthday.

1. Configure a rule in Brevo: "7 days before birthday → attribute reward"
(Brevo Loyalty UI or via the Rules API)
2. Webhook balance_value_updated fires on the birthday event
3. Trigger a personalized birthday email with voucher code from GET .../vouchers

A retailer displays a loyalty status block on the customer account page.

1// Node.js — parallel API calls
2const [balances, tier, vouchers] = await Promise.all([
3 fetch(`${BASE}/subscriptions/${contactId}/balances`, { headers }),
4 fetch(`${BASE}/subscriptions/${contactId}/tier`, { headers }),
5 fetch(`${BASE}/subscriptions/${contactId}/vouchers`, { headers }),
6]);
7
8// Render: "340 pts · Silver · 160 pts to Gold · 1 reward available"

A program where points expire after 12 months. Trigger a winback campaign 30 days before expiry.

1. Configure expiry reminder on balance definition (30 days before expiry)
2. Webhook balance_expiration_reminder fires
3. Trigger automation: "Your 340 points expire in 30 days — shop now"
4. If member doesn't act: webhook balance_value_expired fires
5. Trigger winback: "Your points expired — here's 50 bonus points to come back"
→ POST .../transactions (amount: 50, autoComplete: true)

Additional resources

ResourceLink
Loyalty API Reference — ProgramAPI reference
Loyalty API Reference — BalanceAPI reference
Loyalty API Reference — TierAPI reference
Loyalty webhooksGuide
eCommerce orders guideGuide
Contacts API guideGuide
AuthenticationGuide
Rate limitsGuide
Webhooks getting startedGuide
Brevo Help Center — Set up a loyalty programhelp.brevo.com