Available

Receive webhooks

Have your project call your server when a person is created, a session ends or a sign-in fails, and verify that each call is genuine.

A webhook is an HTTP call from your project to your server when something happens. Use webhooks to keep another system current: create a workspace when a person is created, or close a person's sessions elsewhere when they are suspended.

Before you begin

You need:

  • An https URL on your server. The URL must be public: a webhook cannot be pointed at a private or internal address.
  • A handler at that URL that can read the raw request body and answer within 10 seconds.
  • Access to Integrations > Connections in the AuthFI console.

Register the webhook

In the console, open Integrations > Connections and choose Connect something. Under Webhooks, choose Custom webhook. The Register a webhook dialog opens.

FieldEnter
Delivery URLThe https URL on your server
EventsAll events, or Choose events and the events you want. See Events.

Choose Register.

Store the signing secret

The dialog now shows the signing secret, under the title Its signing secret, shown once. Copy the secret and store it with your server's other secrets. The signing secret is how you tell a genuine call from a forged one.

Choose I've stored it. The webhook appears as a card on the Connections tab.

Verify the signature

Every call carries a signature in the X-AuthFI-Signature-256 header. The signature is an HMAC-SHA256, keyed with your signing secret, of three parts joined together: the value of X-AuthFI-Timestamp, a dot, and the exact body you received.

Compute the same value on your server and compare the two.

import { createHmac, timingSafeEqual } from "node:crypto";

// `rawBody` is the bytes as received, before any JSON parsing.
export function genuine(rawBody, headers, secret) {
  const timestamp = headers["x-authfi-timestamp"];
  const expected = "sha256=" + createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest("hex");
  const given = headers["x-authfi-signature-256"] ?? "";
  return expected.length === given.length && timingSafeEqual(Buffer.from(expected), Buffer.from(given));
}

secret is the signing secret you stored. headers are the request headers, and the body is the bytes as they arrived.

Check the signature before you do anything else. Reject a call whose signature does not match, and reject a timestamp more than a few minutes old.

Answer within 10 seconds

Answer with any 2xx status within 10 seconds. Do the work afterwards: accept the call, queue it, and return.

Check that it works

Cause one of the events you chose. For example, with Group member added selected, add a person to a group in the console.

Your server receives a POST whose X-AuthFI-Event header is group.member_added, and your signature check passes.

Events

EventLabel in the consoleWhen
user.createdUser createdA person was added
user.updatedUser updatedA person's details changed
user.deletedUser deletedA person was removed
user.blockedUser blockedA person was suspended
user.mfa_enrolledUser enrolled MFAA person set up multi-factor authentication (MFA)
login.successLogin succeededSomeone signed in
login.failedLogin failedA sign-in failed
session.revokedSession revokedA session was ended
group.member_addedGroup member addedA person joined a group
group.member_removedGroup member removedA person left a group
organization.createdOrganization createdAn organization was created
role.assignedRole assignedA role was given to someone

What your server receives

Each call is a POST with a JSON body.

{
  "id": "9b2f6c1e-...",
  "event": "group.member_added",
  "timestamp": "2026-10-10T09:14:07Z",
  "data": {
    "actor": "user:[email protected]",
    "subject": "user:[email protected]",
    "resource_type": "group",
    "resource_id": "engineering"
  }
}

The call carries these headers:

HeaderValue
X-AuthFI-EventThe event name
X-AuthFI-DeliveryAn ID for this delivery. The same across retries.
X-AuthFI-TimestampWhen it was sent
X-AuthFI-Signature-256sha256= and the signature, in hex

How deliveries are retried

Your answer decides whether AuthFI sends the call again.

Your answerWhat happens
2xxDelivered
5xx, 408, 429, or no answer in 10 secondsSent up to 6 times in all: the first attempt, then again after 2, 4, 8, 16 and 32 minutes

A retry carries the same X-AuthFI-Delivery as the first attempt. Store the IDs you have handled, and ignore a delivery you have already handled.

Order of deliveries

Deliveries can arrive in any order. When order matters, sort by timestamp in the body.

Pause or remove a webhook

On Integrations > Connections, open the Connections tab and find the webhook's card.

  • To stop deliveries for a while, choose Pause and confirm with Pause deliveries. To start them again, choose Resume and confirm with Resume deliveries.
  • To remove the webhook, choose Disconnect and confirm with Disconnect.

Troubleshooting

What you seeCauseFix
The same delivery arrives againYour server answered 5xx, 408 or 429, or took over 10 secondsAnswer 2xx first and do the work afterwards
One event is handled twiceA retry was treated as a new deliveryIgnore an X-AuthFI-Delivery you have already handled
The signature never matchesThe body was parsed or changed before the checkCompute the HMAC over the bytes as received
Events are handled in the wrong orderDeliveries can arrive in any orderSort by timestamp

Next steps