breezeauth.com
EN
Sign inGet started free
WebhooksReceive events reliably

Receive events reliably

The rules every webhook endpoint must follow, covering authenticity, organization checks, duplicates, fast acknowledgment and fetching data from the API.

On this page
  1. Summary
  2. Verify the signature
  3. Check the organization
  4. Ignore duplicates
  5. Acknowledge quickly, process later
  6. Fetch the details from the API
  7. Reconcile periodically
  8. Checklist

This page describes how to build an endpoint that receives BreezeAuth webhooks safely and without losing or double-processing events. It is written for developers, but the summary below is useful for anyone responsible for an integration.

Summary

For every request it receives, your endpoint must:

  1. Verify the signature. Reject the request if it is missing or invalid.
  2. Check the organization. Reject the message if organizationsUuid is not the organization you expect.
  3. Ignore duplicates. If you have already received this webhook-id, reply 2xx and stop.
  4. Store the message and reply 2xx immediately, well within 15 seconds.
  5. Process the message in the background: fetch the current data from the API and update your system.
POST /webhooks/breezeauth
  ├─ signature invalid?            → 401, stop
  ├─ organizationsUuid unexpected? → 403, stop
  ├─ webhook-id already seen?      → 204, stop
  ├─ save message + webhook-id     (one transaction)
  └─ 204                           → a background worker processes the saved message

Verify the signature

The signature is the only proof that a message was sent by BreezeAuth. Check it before reading anything else from the message, and reject the request with a 4xx status if it fails. All the details, with code examples, are in Verify signatures.

Check the organization

A valid signature proves that the message comes from BreezeAuth, but BreezeAuth serves many organizations, and every one of them signs with the same keys. Your endpoint must also make sure the message is about your organization.

Compare the organizationsUuid field of the body with the identifier of the organization your system expects, and reject the message if they differ. This protects you, for example, if your endpoint URL is registered by mistake, or on purpose, as a webhook in another organization.

If one endpoint serves several organizations, for example in a multi-tenant product, keep the list of organization identifiers it accepts and look each message up in that list.

Note: your organization’s identifier is the organizationsUuid value. It is shown under the organization’s name on the Manage Organization page, with a button to copy it.

Ignore duplicates

BreezeAuth delivers each event at least once. In some situations the same message can arrive more than once, for example:

  • your endpoint processed the message, but its response took longer than 15 seconds or was lost on the network, so BreezeAuth considered the attempt failed and retried;
  • a temporary failure inside BreezeAuth’s infrastructure caused a message that had already been delivered to be sent again;
  • two webhooks in your organization point to the same URL and subscribe to the same event.

Duplicate detection on your side is therefore mandatory. Each message has a unique identifier, sent in the webhook-id header and repeated as uuid in the body. It is the same across every delivery of the same event.

A simple and robust approach is a table with a unique constraint on the identifier:

CREATE TABLE breezeauth_webhook_messages (
  webhook_id  VARCHAR(64) PRIMARY KEY,
  received_at TIMESTAMP   NOT NULL,
  payload     TEXT        NOT NULL,
  processed_at TIMESTAMP  NULL
);

When a message arrives, insert its identifier and payload in a single transaction. If the insert fails because the identifier already exists, the message is a duplicate: reply 2xx without doing anything else. Keep the identifiers for at least a few days; keeping them longer costs little and makes your audit trail more complete.

Tip: reply 2xx to duplicates, not an error. An error would make BreezeAuth retry a message you have already received.

Acknowledge quickly, process later

BreezeAuth waits at most 15 seconds for a response. Your endpoint should reply as soon as the message is safely stored, and leave the actual work (calling the API, updating databases, sending e-mails) to a background process such as a job queue, a worker or a scheduled task.

  • Reply with any 2xx status, such as 200 OK or 204 No Content. The response body is ignored.
  • Don’t call the BreezeAuth API or other slow services before replying.
  • If your endpoint can’t store the message (for example, your database is down), reply with a 5xx status so BreezeAuth retries it.

Doing heavy work synchronously is the most common cause of timeouts, and therefore of retries and duplicates.

Fetch the details from the API

Messages only carry identifiers. When your background process needs more information, it reads the current state from the BreezeAuth API, at https://api.breezeauth.com.

  1. In the admin console, create a service user under Users › Service Users and issue a credential for it. You get a client ID and a client secret. Give the service user only the access your integration needs, for example the Auditor role for read-only access. See Service users.

  2. Exchange the credential for an access token:

    curl -X POST https://api.breezeauth.com/oauth/token \
      -H 'Content-Type: application/json' \
      -d '{"grant_type": "client_credentials", "client_id": "<client ID>", "client_secret": "<client secret>"}'

    The response contains an access_token and its lifetime in seconds, expires_in. Reuse the token until it expires, then request a new one. See Access tokens for details and code examples.

  3. Call the API with the token in the Authorization header:

    curl https://api.breezeauth.com/organizations/<organizationsUuid>/memberships/<membershipsUuid> \
      -H 'Authorization: Bearer <access token>'

Where to read each resource:

Events API request
membership.* GET /organizations/{organizationsUuid}/memberships/{membershipsUuid}
group.* GET /organizations/{organizationsUuid}/groups/{groupsUuid}
group_assignment.* GET /organizations/{organizationsUuid}/groups/{groupsUuid}/assignments/{groupAssignmentsUuid}
application.* GET /organizations/{organizationsUuid}/applications/{applicationsUuid}
application_assignment.* GET /organizations/{organizationsUuid}/applications/{applicationsUuid}/assignments/{applicationAssignmentsUuid}
domain.* GET /organizations/{organizationsUuid}/domains/{domainsUuid}

Things to keep in mind:

  • Deleted resources. After a *.deleted event, the resource is usually no longer available and the API responds with 404 Not Found. Treat that as confirmation of the deletion.
  • Order is not guaranteed. Messages can arrive in a different order than the events happened, for example a membership.updated before the matching membership.created. Because you always read the current state from the API, the order doesn’t matter: process each message as “something changed on this resource, let me look at it now”.
  • Several messages, one read. When many events about the same resource arrive close together, your background process can read the resource once and handle them all.

Reconcile periodically

Webhooks keep your system up to date in near real time, but they are not a full synchronization mechanism. If your endpoint is unavailable for longer than the retry window, or if a webhook is disabled for a while, some events will not be delivered. BreezeAuth does not currently offer a way to resend them.

For integrations where missing a change matters, such as removing access of people who left the company, add a periodic reconciliation: for example, once a day, list the relevant resources from the API and fix any difference with your system. Webhooks then handle the day-to-day changes, and reconciliation catches whatever slipped through.

Checklist

  • The endpoint uses https:// and the URL registered in BreezeAuth is the final address (no redirects).
  • Optionally, the firewall only accepts requests from BreezeAuth’s IP addresses.
  • The signature is verified on the raw body before anything else.
  • Messages for an unexpected organizationsUuid are rejected.
  • webhook-id is stored with a unique constraint, and duplicates are answered with 2xx.
  • The endpoint replies within a few seconds and processes messages in the background.
  • Unknown event types are acknowledged with 2xx and ignored.
  • Details are fetched from the API, and 404 after a deletion is handled.
  • A periodic reconciliation exists for critical data.
  • Someone checks the event history when deliveries fail.

Last updated: October 13, 2026