Automatically notify your app any time certain changes are made.
Subscribe your app to changes in Connections by registering incoming webhooks to receive Connection events.
{ "event": "connection.activated", "id": "event_10FKJ843CVE8F7BXQSPFH0M53V", "data": { "object": "connection", "id": "conn_01EHWNC0FCBHZ3BJ7EGKYXK0E6", "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", "state": "active", "connection_type": "OktaSAML", "name": "Foo Corp's Connection", "created_at": "2021-06-25T19:07:33.155Z", "updated_at": "2021-06-25T19:07:33.155Z", "domains": [ { "id": "conn_domain_01EHWNFTAFCF3CQAE5A9Q0P1YB", "object": "connection_domain", "domain": "foo-corp.com" } ] }, "created_at": "2021-06-25T19:07:33.155Z" }
Example use case: Enable SSO for the Connection Organization.
{ "event": "connection.deactivated", "id": "event_11FKJ843CVE8F7BXQSPFH0M53V", "data": { "object": "connection", "id": "conn_01EHWNC0FCBHZ3BJ7EGKYXK0E6", "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", "state": "inactive", "connection_type": "OktaSAML", "name": "Foo Corp's Connection", "created_at": "2021-06-25T19:07:33.155Z", "updated_at": "2021-06-25T19:07:33.155Z", "domains": [ { "id": "conn_domain_01EHWNFTAFCF3CQAE5A9Q0P1YB", "object": "connection_domain", "domain": "foo-corp.com" } ] }, "created_at": "2021-06-25T19:07:33.155Z" }
Example use case: Disable SSO for the Connection Organization.
{ "event": "connection.deleted", "id": "event_12FKJ843CVE8F7BXQSPFH0M53V", "data": { "object": "connection", "id": "conn_01EHWNC0FCBHZ3BJ7EGKYXK0E6", "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", "state": "inactive", "connection_type": "OktaSAML", "name": "Foo Corp's Connection", "created_at": "2021-06-25T19:07:33.155Z", "updated_at": "2021-06-25T19:07:33.155Z", "domains": [ { "id": "conn_domain_01EHWNFTAFCF3CQAE5A9Q0P1YB", "object": "connection_domain", "domain": "foo-corp.com" } ] }, "created_at": "2021-06-25T19:07:33.155Z" }
Example use case: Delete SSO for the Connection Organization.
There is currently no rate limiting on event deliveries.
Webhooks should use HTTPS and expect to receive POST requests with the following headers:
WorkOS sends the header as WorkOS-Signature
, but many web servers will normalize all HTTP request headers to their lowercase variants. In this case, you’ll extract the workos-signature
header.
See example approaches for implementing a webhook endpoint below.
import type { NextApiRequest, NextApiResponse } from 'next'; export default (req: NextApiRequest, res: NextApiResponse) => { if (req.method === 'POST') { const payload = req.body; const sigHeader = req.headers['workos-signature']; // Verify the signature and process the event res.status(200); } };
Set and save the webhook URL in the WorkOS Dashboard, so WorkOS knows where to deliver the events.
The Webhook Secret is used to verify webhook requests from WorkOS. Be sure to keep the value secure.
In order to avoid unnecessary retry requests hitting your webhook handler, we recommend using two concurrent processes for handling events: one for receiving the event, and the other for processing it.
On receiving an event, you should respond with an HTTP 200 OK
to signal to WorkOS that the event was successfully delivered. Otherwise, WorkOS will consider the event delivery a failure and retry up to 12 times, with exponential backoff over 3 days.
Before processing the request payload, verify the request was sent by WorkOS and not an unknown party.
WorkOS includes a unique signature in each webhook request that it sends, allowing you to verify the authenticity of the request. In order to verify this signature, you must obtain the secret that is generated for you when you set up your webhook endpoint in the WorkOS dashboard. Ensure that this secret is stored securely on your webhook endpoint server as an environment variable.
The WorkOS SDKs have methods for validating the timestamp and signature of a webhook. Examples using these methods are included below. The parameters are the payload (raw request body), the WorkOS-Signature
header, and the Webhook Secret. There is an optional parameter, tolerance, that sets the time validation for the webhook in seconds. The SDK methods have default values for tolerance, usually 3-5 minutes.
import WorkOS from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); const webhook = workos.webhooks.constructEvent({ payload: payload, sigHeader: sigHeader, secret: process.env.WEBHOOK_SECRET, });
If implementing webhook validation yourself, you’ll need to use the following steps:
First, extract the timestamp and signature from the header. There are two values to parse from the WorkOS-Signature
, delimited by a ,
character.
issued_timestamp
: The number of milliseconds since the epoch time at which the event was issued, prefixed by t=
signature_hash
: The HMAC SHA256 hashed signature for the request, prefixed by v1=
To avoid replay attacks, we suggest validating that the issued_timestamp
does not differ too much from the current time.
Next, construct the expected signature. The expected signature is computed from the concatenation of:
issued_timestamp
.
characterHash the string using HMAC SHA256, using the Webhook Secret as the key. The expected signature will be the hex digest of the hash. Finally, compare signatures to make sure the webhook request is valid.
Once you’ve determined the event request is validly signed, it’s safe to use the event, i.e. the request body, in your application’s business logic. You do not need to signal to WorkOS whether or not the event was processed successfully.