Skip to main content

Webhooks

A webhook is an outbound HTTP POST that Unioo sends to a URL you own when a bank-side domain event occurs — a lead is accepted, a report is published, a review is approved, and so on. Webhooks let you keep your own systems in sync with Unioo.

Webhook subscriptions are configured in the Unioo bank admin site, not through the External API. See Managing webhook subscriptions for the UI walkthrough and Receiving webhook deliveries for how to build a receiver.

Subscription model

A subscription is the pairing of:

  • One receiver URL that you control.
  • One or more event groups the subscription is interested in.
  • One bank — every subscription belongs to the bank that the calling user is authenticated against.
  • An authentication option that tells Unioo how to authenticate to your receiver.
  • An isActive flag — when false, events for the subscription are recorded as WebhookInactive and not delivered. Use this to pause a subscription without losing its configuration.

A subscription targets event groups, not individual event types. Subscribing to BankReportEvent enrols you in every REPORT_* type — current and future. If you only want a subset, filter on the type field in your receiver.

You can have multiple subscriptions per bank pointing at different URLs (for example, one for production and one for a staging environment).

Event catalog

Every delivery carries a type string that names the event and a payload object whose shape depends on the event group. There are six groups today:

LeadEvent

Payload: { "leadId": "<uuid>" }

TypeFired when
LEAD_CREATEDA new lead is created (e.g. during onboarding).
LEAD_ACCEPTEDA lead is accepted.
LEAD_REJECTEDA lead is rejected.

OrganizationEvent

Payload: { "organizationId": "<uuid>" }

TypeFired when
ORGANIZATION_CREATEDAn organization is registered with the bank.
ORGANIZATION_SURRENDEREDAn organization is surrendered (released from the bank).
ORGANIZATION_MARKETING_CONSENT_CHANGEDAn organization's marketing consent flag changes.

InvitationEvent

Payload: { "invitationId": "<uuid>" }

TypeFired when
INVITATION_SURRENDEREDAn outstanding organization invitation is surrendered.

BankReportEvent

Payload: { "reportId": "<uuid>" }

TypeFired when
REPORT_PUBLISHEDA bank report (package) is published to the bank.
REPORT_REJECTEDA published bank report is rejected.

BankReportReviewEvent

Payload: { "reviewId": "<uuid>", "reportId": "<uuid>" }

TypeFired when
REPORT_REVIEW_CREATEDA review is opened for a published report.
REPORT_REVIEW_PROVISIONALLY_APPROVEDA reviewer provisionally approves the report.
REPORT_REVIEW_APPROVEDA reviewer fully approves the report.
REPORT_REVIEW_REJECTEDA reviewer rejects the report.

See Reviews for the underlying state machine.

BankRequestEvent

Payload: { "bankRequestId": "<uuid>" }

TypeFired when
BANK_REQUEST_FULFILLEDA pending bank request is fulfilled by the organization.
BANK_REQUEST_APPROVEDA fulfilled bank request is approved by the bank.
BANK_REQUEST_REJECTEDA bank request is rejected.

Authentication options

A subscription has exactly one authentication option, chosen at creation time:

  • None — every delivery includes an X-Unioo-Secret header carrying the subscription's secret (a WH--prefixed string shown once when the subscription is created in the admin UI, and again whenever you regenerate it — see Managing webhook subscriptions). Your receiver compares this header against the value you stored.
  • Entra — Unioo obtains an OAuth 2.0 token from your Entra (Azure AD) tenant using a confidential-client flow with credentials you provide (clientId, clientSecret, tenantId, tokenScope) and sends it as Authorization: Bearer <token>. Your receiver validates the JWT the same way it validates any other inbound Entra token. Stored credentials are encrypted at rest; only the last four characters of the client secret are ever returned via the API (clientSecretSuffix).

X-Unioo-Secret is sent on every delivery regardless of authentication choice, so even Entra receivers can additionally verify the shared secret if they wish.

Delivery semantics

  • At-least-once. A given event may be delivered more than once. Make your receiver idempotent — the eventId field is unique per delivery attempt sequence and is the recommended dedup key.
  • No ordering. Two events for the same entity can arrive in any order, especially across retries. If order matters, treat the webhook as a signal and re-fetch the current state of the entity via the GraphQL API before acting.
  • Success = HTTP 2xx. Any non-2xx response, network error, or timeout marks the delivery as failed and schedules a retry. Retries are backed by Hangfire; expect repeated attempts on a transient failure.
  • Per-subscription failure isolation. A failure to deliver to one subscription does not affect deliveries to other subscriptions for the same event.

Where to go next