Integrations and ConnectorsConnectorsSetup GuidesWebhooks

Audit Webhook Connector

Descope's Audit Webhook Connector allows Descopers to stream audit logs to their own API.

1. Deploy an API to Capture Webhook Events

Deploy an API to a service of your choice that accepts HTTP requests. This API will be used to receive the audit logs from Descope.

This connector sends outbound requests from Descope's static IPs. Allowlist these on your API gateway, firewall, or server as needed.

The connector posts a JSON array of audit events, not a single event object. Your handler must iterate over the array:

app/api/audit/route.tsx
export async function POST(request: Request) {
    // The body is an array of audit events, even when it contains a single event.
    const events = await request.json();

    for (const event of events) {
        // Handle each audit log
    }

    return new Response(null, { status: 204 });
}

Return a 2xx status only after you have successfully processed the batch. Descope treats any 2xx response as a successful delivery and will not resend those events.

2. Configuring the Webhook Audit Connector

Navigate to the Descope's Audit Webhook Connector configuration page and fill in the required parameters:

  • Connector Name: Provide a unique name for your connector. This assists in distinguishing it, especially when multiple connectors are derived from the same template.
  • Connector Description: Briefly explain the purpose of this connector.
  • Base URL: Input the API URL where you'd like to send audit events to. This should start with either http:// or https://. Use the URL from Step 1.
  • Authentication Type: Descope supports various methods to authenticate with your service. Choose the method that suits your API:
    • Bearer Token: Used for access keys such as JWTs.
    • API Key: This usually involves a key-value pair.
    • Basic Authentication: The traditional username and password method.
    • None: Select this if your API doesn't require any authentication.
  • Headers (Optional): Some APIs need specific headers, usually key-value pairs, to provide more details about the impending action.
  • HMAC Secret (Optional): HMAC is a symmetric key method for message signing. The provided secret will be used to sign the payload. The outcome signature will be sent in the x-descope-webhook-s256 header. The recipient service should use this secret to validate the payload's integrity and authenticity by verifying the supplied signature.
  • Trust Any Certificate: By default, this option is turned off. If enabled, the client will overlook any certificate errors. While convenient for testing, it's crucial to remember that this is an insecure choice for production.
  • Stream Audit Events: Select this if you want to stream audit events to your API.

Twilio connector widget input

3. View Audit Logs

Once you've configured the Audit Webhook Connector, your events will be sent to the API you specified. You can view the audit logs in the Audit page, which should match the events you receive in your API.

Twilio connector widget input

Twilio connector widget input

Each event includes the originating client IP as a top-level remoteAddress field. See Audit Event remoteAddress Field.

Payload Contract

Request Envelope

Each delivery is an HTTP POST whose body is a JSON array of audit events. A single request contains up to 100 events, so your endpoint must handle batches, not just individual events.

[
    { "action": "Change Password", "...": "..." },
    { "action": "UserModified", "...": "..." }
]

Event Fields

Every event in the array contains the following top-level fields. Field names are camelCase.

FieldTypeDescription
actionstringThe action performed. See Audit Events for the full list. Some action values contain spaces — for example, Change Password.
actorIdstringIdentifies who performed the action.
userIdstringThe destination user the action was performed on.
projectIdstringThe Descope project the event belongs to.
loginIdsarray of stringsThe login identifiers associated with the user.
occurrednumberTime of the event as a Unix epoch timestamp in milliseconds.
occurred_formattedstringThe same timestamp as an RFC 3339 string, for example 2026-06-08T20:18:25.843277Z.
devicestringSource device, such as Desktop or Mobile. Can also reflect the SDK used, such as NodeJS.
methodstringThe authentication method used. May be an empty string.
geostringTwo-letter country code for the origin of the request.
remoteAddressstringIP address (v4/v6) of the origin of the request.
tenantsarray of stringsAssociated tenant IDs. May be null.
dataobjectRequest context and event-specific details. See The data object.

Timestamps are numbers

occurred is a number, not a string. Passing it directly to a date parser that expects ISO 8601 will fail. Either divide by 1000 and parse as epoch seconds, parse the millisecond value directly, or use the occurred_formatted companion field.

The data object

Client and request context is nested inside data, not at the top level. Common keys include:

KeyTypeDescription
browserstringBrowser detected from the request.
osstringOperating system detected from the request.
osVersionstringOperating system version.
devicestringDevice type, repeated from the top-level field.
correlation_idstringMatches the flow execution ID in troubleshooting logs. See Correlating Audit and Troubleshooting Logs.
flow_idstringThe flow that produced the event.
flow_execution_idstringThe specific flow execution.
request_detailsobjectThe request uri and method.
ChangeobjectPresent on modification events such as UserModified. Describes what changed.

The remaining contents of data vary by action. Treat it as an open map and read only the keys you need.

Custom attribute timestamps

Timestamps inside custom attributes are also delivered as epoch-millisecond numbers, and they do not have a _formatted companion field. For example, a lastPasswordReset custom attribute arrives as "custom_attribute_lastPasswordReset": 1780949905852.

Example Payloads

[
    {
        "action": "Change Password",
        "actorId": "U3Dga0ziehqoVAyXog2H6AaknVFn",
        "userId": "U3Dga0ziehqoVAyXog2H6AaknVFn",
        "projectId": "P2ABc0defGHijKLmnOPqrSTuvWXyz",
        "occurred": 1780949905843,
        "occurred_formatted": "2026-06-08T20:18:25.843277Z",
        "method": "",
        "device": "Desktop",
        "geo": "US",
        "loginIds": ["user@company.com"],
        "remoteAddress": "203.0.113.42",
        "tenants": null,
        "data": {
            "browser": "Chrome",
            "correlation_id": "3ErwHAgTHSy1pwg0SgN9eFcNIsu",
            "count": 0,
            "device": "Desktop",
            "flow_execution_id": "3ErwG1GLbW4tWI3nAKRD5zO09E9",
            "flow_id": "reset-password",
            "os": "macOS",
            "osVersion": "10.15.7",
            "request_details": {
                "uri": "/v1/flow/next",
                "method": "POST"
            }
        }
    }
]

Differences From the Search Audit API

The Audit Webhook and the Search Audit API push the same events, but they do not populate them identically in the event itself.

It's important to make sure you do not use one as a reference for the other.

ConceptAudit WebhookSearch Audit API
Login identifiersloginIdsexternalIds
Time of the eventoccurred, a JSON numberoccurred, a JSON string
ISO timestampoccurred_formattedNot included
Event IDNot includedID
Event severityNot includedtype
Everything elseaction, actorId, userId, projectId, device, method, geo, remoteAddress, tenants, dataSame names

The two most commonly overlooked differences are the following:

  • The login identifier key is not the same: The webhook sends loginIds; the API returns externalIds.
  • occurred is a number on the webhook and a string from the API: Both are Unix epoch milliseconds, but the JSON types are different.

Ensure that you're normalizing these values appropriately as you see fit, when ingesting these audit events.

Delivery Behavior

Audit events are not delivered in real time. Descope persists audit events first, then a background loop periodically collects pending events and streams them to your endpoint in batches.

Consequences to design for:

  • Expect delays: A gap of several minutes between the action and the webhook delivery is normal. Under backlog or retry conditions, the gap can be considerably longer.
  • Events arrive grouped: Actions spanning a window of time are typically delivered together in a single batch, in one request.
  • Do not rely on the webhook for synchronous flow: If your product needs to reflect an event to an end user immediately, drive that from your own application logic and do not rely on events from the webhook.
  • Reconcile periodically: For critical data, use the Search Audit API as a fallback to catch events that a failed delivery may have missed.

Note

Delivery timing is not a guaranteed service level and can vary with event volume and system load. Do not hard-code a timeout based on observed delays.

Use Cases

Roles Revoked From A User

As a security best practice, monitoring changes in the association of roles to users is crucial. As the identity provider, Descope can provide the role name as part of the "UserModified" audit event.

The data object of that event contains:

{
    "Change": {
        "removed_multi_tenant_roles": {
            "T2lTcqSm7f5GD8lRwCyo4aHvC6wo": [
                {
                    "id": "ROL2lKYLkbHroW5IntCAYTWPX86rRs",
                    "name": "test1"
                }
            ]
        }
    },
    "browser": "Chrome",
    "correlation_id": "2nQJ0PEyu5EHrMJChqNgPC4AeVp",
    "device": "Desktop",
    "os": "macOS",
    "osVersion": "10.15.7"
}

In this example the role name "test1" was removed from the user.

Flow Modified In Production

Monitoring when authentication flows are modified is critical for maintaining security and compliance. As the identity provider, Descope can provide the modified flow and associated user as part of the "FlowUpdated" audit event.

{
    "action": "FlowUpdated",
    "actorId": "U2mLZYMlcIsqX6JouDBZUXQY97sSt",
    "loginIds": ["user@company.com"],
    "occurred": 1705329022000,
    "occurred_formatted": "2024-01-15T14:30:22Z",
    "device": "Desktop",
    "data": {
        "browser": "Chrome",
        "correlation_id": "3mQK1PFzv6FIsNKDhrOhQD5BfWq",
        "flow_id": "FL2mLZYMlcIsqX6JouDBZUXQY97sSt",
        "flow_name": "Production Login Flow",
        "os": "macOS",
        "osVersion": "10.15.7",
        "changes": {
            "modified_steps": ["email_verification", "mfa_setup"]
        }
    },
    "os": "macOS",
    "osVersion": "10.15.7",
    "remoteAddress": "203.0.113.42"
    ...
}

In this example the user user@company.com modified the "Production Login Flow" from 203.0.113.42.

Project Setting Modified

Tracking changes to project-level configurations helps maintain security posture and compliance. Descope can provide the modified setting and associated actor as part of the "ProjectSettings" audit event.

{
    "action": "ProjectSettings",
    "actorId": "U2QGzw7GJtOLEisPiRE6CgXrLm4",
    "loginIds": ["admin@company.com"],
    "occurred": 1705337118000,
    "occurred_formatted": "2024-01-15T16:45:18Z",
    "device": "Desktop",
    "data": {
        "browser": "Chrome",
        "correlation_id": "4nRL2QGzw7GJtOLEisPiRE6CgXr",
        "os": "Windows",
        "osVersion": "11",
        "settings_changed": {
            "jwt_expiration": {
                "old_value": "3600",
                "new_value": "7200"
            },
            "allowed_origins": {
                "added": ["https://newapp.company.com"],
                "removed": []
            }
        }
    }
}

In this example the user admin@company.com edited the "jwt_expiration" and "allowed_origins" settings.

Was this helpful?

On this page