> For the complete documentation index, see [llms.txt](https://help.openloyalty.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.openloyalty.io/implementation-guide/module-checklists/webhooks-and-crm-sync.md).

# Webhooks & CRM Sync

Choosing which Open Loyalty events to subscribe to, receiving webhooks reliably, and keeping CRM and marketing automation in step with the programme.

Webhooks are how Open Loyalty tells your other systems what happened: a member registered, earned, redeemed, changed tier. CRM and marketing automation use them to keep a copy of loyalty data and to trigger messages. This page covers which events to subscribe to and how to receive them reliably.

For the Admin Panel side, see [Webhook configuration](https://help.openloyalty.io/integrations-and-data-exchange/webhooks/configuration) and [HMAC](https://help.openloyalty.io/integrations-and-data-exchange/webhooks/hmac). For developer background, see the Developer Guide's [webhooks overview](https://help.openloyalty.io/technical-guide/webhooks/overview).

## Who does what

| Area                           | Responsibility                                                                                                               |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **Admin Panel / API**          | Webhook subscriptions per event, endpoint URL or SQS queue, HMAC signing, custom headers — set up in each environment        |
| **Integration layer**          | A fast, always-on endpoint that verifies, queues and acknowledges; workers that deduplicate and update downstream systems    |
| **CRM / marketing automation** | Stores loyalty attributes on the contact and runs journeys (welcome, earn and burn messages, expiry reminders, tier changes) |

## Which events to subscribe to

Subscribe only to what a downstream system uses. Every event is listed in [What triggers a webhook](https://help.openloyalty.io/integrations-and-data-exchange/webhooks/what-triggers-a-webhook).

| Need                                        | Events                                                                                                                                                            |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Keep the contact's loyalty profile in CRM   | `CustomerRegistered`, `CustomerUpdated`, `CustomerDeactivated`                                                                                                    |
| Keep the balance in CRM                     | `WalletBalanceUpdated`                                                                                                                                            |
| Earn messages ("You earned 50 points for…") | `CampaignEffectWasApplied`                                                                                                                                        |
| Redemption messages and vouchers with codes | `CustomerBoughtReward`, `RewardRedemptionStatusChanged`                                                                                                           |
| Expiry reminders                            | `PointsWillExpire`, `CouponWillExpire` — see [Expiring notifications](https://help.openloyalty.io/integrations-and-data-exchange/webhooks/expiring-notifications) |
| Tier messages                               | `CustomerLevelChanged`, `LevelWillExpire`                                                                                                                         |
| Mission progress                            | `MemberChallengeProgressWasChanged`                                                                                                                               |
| Purchase confirmations                      | `TransactionRegistered`, `TransactionAssignedToCustomer`                                                                                                          |
| Refresh cached configuration                | `CampaignCreated`, `CampaignUpdated`, `RewardCreated`, `RewardUpdated`                                                                                            |

## Design decisions

| Decision                      | Options                                                                                                        | Recommendation                                                                                                                    |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Delivery target               | HTTPS endpoint, or [AWS SQS](https://help.openloyalty.io/technical-guide/webhooks/sending-webhooks-to-aws-sqs) | SQS if you're on AWS — the queue absorbs bursts and outages. Otherwise an HTTPS endpoint that only verifies and queues.           |
| Event payload or a fresh read | Use the webhook data, or read the current state from the API                                                   | Use the payload for messages about the event itself; read the API when you need the full, current state (e.g. the whole profile). |
| One endpoint or many          | One endpoint for all events, or one per event                                                                  | One endpoint with routing by `eventName` is simpler to secure and monitor.                                                        |
| Real-time or batch in CRM     | Write each event, or collect and load daily                                                                    | Real-time for balances and transactional messages; batches are fine for journeys that run daily.                                  |

## Recommended flow

{% stepper %}
{% step %}

### Subscribe

Create the subscriptions in the Admin Panel or with `POST /api/{storeCode}/webhook/subscription`, one per event. Turn on **Enable HMAC signature** and store the secret — it's shown only once. Subscriptions aren't part of configuration exports, so set them up in each environment.
{% endstep %}

{% step %}

### Receive

Verify the [HMAC signature](https://help.openloyalty.io/integrations-and-data-exchange/webhooks/hmac/verifying-the-signature) and reject anything that fails. Put the payload on a queue and answer `2xx` straight away — do the real work afterwards.
{% endstep %}

{% step %}

### Process

Webhooks are sent asynchronously. A request that times out or doesn't get a `2xx` is retried a few times and then dropped, and deliveries can repeat or arrive out of order. So in the worker: skip a `messageId` you've already processed, and when order matters, compare `createdAt` or read the current state from the API.
{% endstep %}

{% step %}

### Update downstream

Upsert into CRM by the member identifier — never insert blindly. Keep loyalty data in dedicated fields so a webhook can't overwrite data CRM owns.
{% endstep %}

{% step %}

### Reconcile

Don't rely on webhooks alone for critical data. Compare CRM against Open Loyalty periodically — for example balances for members active that day, or a full data export — and fix differences.
{% endstep %}
{% endstepper %}

## Sequence diagram

<figure><img src="https://226878483-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZ4Fuoa1PTF0DrNTgjzJu%2Fuploads%2Fgit-blob-439f31d39f0f5a91d345a14ce7dcadfa5b59f53f%2Fig-webhooks-delivery.png?alt=media" alt="Open Loyalty posts an HMAC-signed webhook with messageId, eventName, createdAt and data to the integration layer&#x27;s endpoint. The endpoint verifies the signature, puts the payload on a queue and answers 2xx at once; without a 2xx in time Open Loyalty retries a few times and then gives up. A worker takes the message, skips it if the messageId was already processed, optionally reads the current member or wallet from Open Loyalty, and upserts the data into CRM. Failures stay on the queue and are retried." width="728"><figcaption><p>Receiving and processing a webhook</p></figcaption></figure>

## Go-live checklist

### Admin Panel / API

* [ ] Production has its own subscriptions, pointing at production endpoints — not staging.
* [ ] HMAC signing is on and the secret is stored in a secrets manager.
* [ ] Only events with a consumer are subscribed.

### Integration layer

* [ ] The endpoint verifies the signature, queues and answers `2xx` within a second or two.
* [ ] The endpoint is monitored and scaled for launch-day volume.
* [ ] Workers deduplicate by `messageId` and tolerate out-of-order delivery.
* [ ] Failed processing is retried from the queue and alerts after repeated failures.

### CRM / marketing automation

* [ ] Loyalty attributes are stored in dedicated fields and upserted by member identifier.
* [ ] Journeys handle a repeated event without sending the message twice.
* [ ] A reconciliation job compares balances with Open Loyalty.

### Validation

* [ ] Register, earn, redeem and expire with a test member: each event arrives once and updates CRM.
* [ ] Take the endpoint down briefly: events during the outage are recovered by reconciliation.
* [ ] Send a request with a wrong signature: it's rejected.

## Key endpoints

* `POST /api/{storeCode}/webhook/subscription` — [Subscribe to a webhook](https://help.openloyalty.io/api-reference/webhook-subscription/subscribe-to-a-webhook)
* `GET /api/{storeCode}/webhook/subscription` — [Get webhook subscriptions list](https://help.openloyalty.io/api-reference/webhook-subscription/get-webhook-subscriptions-list)

**Webhook events:** each event's payload is in the API docs under [Webhook event](https://apidocs.openloyalty.io/#tag/Webhook-event) — e.g. [WalletBalanceUpdated](https://apidocs.openloyalty.io/#tag/Webhook-event/operation/WalletBalanceUpdated), [CampaignEffectWasApplied](https://apidocs.openloyalty.io/#tag/Webhook-event/operation/CampaignEffectWasApplied), [CustomerBoughtReward](https://apidocs.openloyalty.io/#tag/Webhook-event/operation/CustomerBoughtReward).

{% hint style="warning" %}
**Common pitfalls**

* Doing the CRM update inside the webhook request: slow responses lead to retries, duplicates and eventually dropped events.
* Trusting delivery order: a "balance 120" event can arrive after "balance 150".
* Copying staging subscriptions to production by hand and missing one — or leaving one pointing at staging.
* Treating webhooks as the only source of truth: without reconciliation, a missed event stays wrong forever.
  {% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.openloyalty.io/implementation-guide/module-checklists/webhooks-and-crm-sync.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
