Skip to main content
With event streams and Auth0 Actions, you can keep a downstream application provisioned with your Auth0 users without deploying your own middleware. Use an event stream Action for outbound SCIM when you want to:
  • Provision Auth0 users into a SCIM-compliant application as they are created.
  • Keep profile attributes in the downstream application current as users change in Auth0.
  • Deactivate or remove users in the downstream application when they are blocked or deleted in Auth0.
  • Provision to a SCIM server without running your own webhook listener.

Implementation overview

This implementation uses an event stream with our outbound SCIM 2.0 user provisioning Action template. Auth0 publishes an event each time a user profile is created, updated, or deleted. The event stream delivers that event to the Action, and the Action sends a matching SCIM 2.0 request to the server you configure. The Action correlates each Auth0 user with its SCIM resource using the SCIM externalId attribute (RFC 7643), which lets a client store its own identifier on a SCIM resource. The Action sets externalId to the Auth0 user_id, which is stable across profile changes. This allows the Action to find the right SCIM resource even after a name or email update.
Auth0 eventSCIM request
user.createdPOST /Users to create the SCIM resource.
user.updatedGET /Users?filter=externalId eq "..." to find the resource, then PUT /Users/{id} to replace it (or PATCH /Users/{id}, if configured).
user.deletedDELETE /Users?filter=externalId eq "..." to find the resource, then DELETE /Users/{id} to delete it. The SCIM server decides whether a DELETE means a hard delete, a tombstone, or a deactivation.
The Action treats the SCIM server’s responses as the source of truth for correlation, so it validates each successful (2xx) response’s compliance with the SCIM 2.0 protocol and only acts on valid results.

Error handling

When the Action throws an error, the delivery is marked failed and Auth0 automatically retries the failed event. After the retries are exhausted, the event becomes available in the dead-letter queue for inspection and redelivery. Because this Action throws errors on invalid responses and terminal SCIM errors, those failed syncs are visible through retries and the dead-letter queue. A change that never produces a delivered event is not visible this way.

Limits

  • This Action synchronizes user profiles and does not include group provisioning.
  • One event stream binds to one Action destination. To target several SCIM servers, create several event streams and Actions.

Instructions

1

Prerequisites

Before you begin, you need:
  • An Auth0 tenant with Events and Actions enabled.
  • A downstream SCIM 2.0 endpoint that accepts POST, GET, PUT, and DELETE on /Users.
  • A SCIM server.
    • The SCIM server must return responses compliant with the SCIM 2.0 protocol (RFC 7644), and specifically:
      • A create (POST /Users) must return a User resource with a string id.
      • A filter query (GET /Users?filter=...) must return a ListResponse with a Resources array, and each returned resource must include a string id. An empty Resources array is valid and means no match.
      On invalid responses, the Action throws an error that includes the text “invalid response shape”.
    • The SCIM server must support an externalId eq filter. If the server rejects the externalId eq filter, the Action falls back to a userName filter, which matches on name and therefore cannot track an email change.
  • A bearer token issued by the SCIM server with permission to read, create, replace, and delete users.
  • Network access from Auth0 to the SCIM server. If the SCIM server restricts inbound traffic, allow calls from the Auth0 IP addresses for your region.
2

Start creating the event stream

From Auth0 Dashboard > Event Streams, select + Create Event Stream to go to the New Event Stream page.In the Destinations section, select Auth0 Actions, then:
  • In the Configurations section, for Stream Name, enter a descriptive name (like “Outbound SCIM provisioning”).
  • In the Select Events section, select user.created, user.deleted, and user.updated.
3

Configure Action secrets

In the Actions Editor, select Secrets (the key icon) and add the secrets for your SCIM server:
SCIM_BASE_URL
string
required
Base URL of the SCIM 2.0 endpoint. For example, https://api.example.com/scim/v2.
SCIM_BEARER_TOKEN
string
required
Bearer token issued by the SCIM server.
SCIM_TIMEOUT_MS
integer
default:1500
Per-request timeout in milliseconds.
SCIM_MAX_RETRIES
integer
default:1
Retry count on HTTP 429, HTTP 5xx, and network errors.
SCIM_CONNECTION_ALLOWLIST
string
Comma-separated connection names. When set, the Action processes events only from these connections.
The transport defaults fit the event stream execution budget. Event Relay enforces an effective execution budget of about 10 seconds, which is tighter than the 20-second Actions ceiling. The default of a 1500-millisecond timeout with one retry keeps the lookup-and-write flow within that budget. Raising either value risks exceeding it.
4

Add the Action template and customize the attribute mapping

In the Actions Editor, copy and paste the outbound SCIM 2.0 user provisioning Action template.The template’s buildScimUser() function maps the Auth0 user profile to the SCIM User resource. The default mapping produces a core SCIM 2.0 User with the following fields:
SCIM attributeAuth0 source
externalIduser_id
activeInverse of blocked
userNameemail
Primary work emailemail
name.givenNamegiven_name
name.familyNamefamily_name
name.formattedname
displayNamename
nickNamenickname
Work phone (optional)user_metadata.phone
You can modify the buildScimUser() function to the mapping your SCIM server expects.
If an Auth0 user does not have the property mapped to the SCIM userName attribute, the Action skips creation and updates for that user and logs a warning.
Additionally, the buildScimUser() function contains a commented block for the SCIM Enterprise User extension. You can uncomment the fields that your server supports and adjust the Auth0 metadata paths for your tenant.
5

Customize user update behavior (optional)

The Action template’s onUserUpdated() function includes commented blocks with opt-in behaviors for user profile updates. Enable them only when your SCIM server requires them:
  • Configure PATCH instead of PUT if your SCIM server does not accept PUT requests. For example, Microsoft Entra ID inbound SCIM omits the put:users permission from its default token and accepts only PATCH.
  • Enable upserts (creates for missing users on updates) if you do not provision existing Auth0 users in your SCIM server before enabling the event stream, or if your SCIM server may otherwise legitimately miss user creation events.
By default, on user profile updates, the Action replaces the full SCIM resource with PUT /Users/{id}. A user.updated event carries the complete user profile and no list of changed fields, so a full replace is the most accurate update.To configure PATCH instead of PUT for user updates, in onUserUpdated(), follow the OPTIONAL: PATCH instead of PUT comment and set the update method and body:
const updateMethod = 'PATCH';
const updateBody = {
    schemas: ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
    Operations: [{ op: 'replace', value: scimUser }],
};
PATCH converges the attribute present in the mapped SCIM body, but unlike PUT, attributes you omit from the body can keep their current downstream values. If you want a removed Auth0 field to clear the downstream value, add explicit remove or null handling that your SCIM server supports.Additionally, this request omits the path attribute because path is optional for replace operations. However, some SCIM servers require a path on every operation. For those servers, send one operation per attribute, and use the schema-qualified path for any extension attribute.
By default, when a user.updated event fires for a user that does not exist in the SCIM server, the Action skips the update and logs a warning. This default assumes that SCIM server should have already processed the earlier user.created event, so a missing user signals an issue.To enable upserts, in onUserUpdated(), follow the OPTIONAL UPSERT comment and replace the skip-and-log with the following line:
return createRemoteUser(config, scimUser, 'created-from-update');
This way, the Action sends a single POST /Users to create the user instead of skipping. The mapped SCIM body already holds the full profile, so the Action needs no follow-up request.
6

Test mapping locally

Before you deploy, you can unit test the Action with a mock event and a stubbed SCIM call. Auth0 documents Jest for this purpose, though you can use any test library.The following Jest test confirms that a user.created event sends a POST /Users request with a bearer token:
const { onExecuteEventStream } = require("./code");

test("user.created sends POST /Users with a bearer token", async () => {
    global.fetch = jest.fn().mockResolvedValue({
        status: 201,
        json: async () => ({ id: "scim-123" }),
    });

    const event = {
        message: {
            type: "user.created",
            id: "evt_1",
            data: { object: { user_id: "auth0|abc", email: "user@example.com" } },
        },
        secrets: {
            SCIM_BASE_URL: "https://api.example.com/scim/v2",
            SCIM_BEARER_TOKEN: "test-token",
        },
    };

    await onExecuteEventStream(event, {});

    expect(global.fetch).toHaveBeenCalledWith(
        "https://api.example.com/scim/v2/Users",
        expect.objectContaining({
            method: "POST",
            headers: expect.objectContaining({ Authorization: "Bearer test-token" }),
        })
    );
});
For development outside the Auth0 Dashboard, you can add type hints with the @auth0/actions package:
/**
 * @typedef {import('@auth0/actions/event-stream/v1').Event} Event
 * @typedef {import('@auth0/actions/event-stream/v1').EventStreamAPI} EventStreamAPI
 *
 * @param {Event} event
 * @param {EventStreamAPI} api
 */
exports.onExecuteEventStream = async (event, api) => {
    // ...
};
Reference the Event Stream trigger types from JSDoc so the file stays plain JavaScript and adds no runtime dependencies.
7

Synchronize existing users (recommended)

Before enabling the event stream, we recommend a one-time synchronization of existing Auth0 users with your SCIM server. The event stream can only react to user events delivered after you enable it, so this one-time synchronization keeps your downstream SCIM server up to date and prevents correlation failures when existing users are updated or deleted.To synchronize existing Auth0 users with your SCIM server:
  1. Get all existing Auth0 users. Use your normal user export or Management API workflow, and include every connection you want to sync.
  2. Reconcile existing SCIM users with Auth0 users. If the SCIM server already has users, match them to Auth0 users by a trusted identifier (such as email), then update each resource so its externalId is the Auth0 user_id. Do not create duplicates.
  3. Backfill missing SCIM users. For each unmatched Auth0 user, send a SCIM create using the same body and attribute mapping defined in the Action.
  4. Verify the baseline. Check sample users, total counts, blocked users, and email changes in the SCIM server.
Enabling the event stream without a one-time synchronization first gradually updates (or trickles) users in your SCIM server. In this situation, you must configure upserts in the Action to create missing users on update events.
8

Save and deploy

Select Save Draft, then Deploy.The Action is now bound to your Event Stream and runs each time a subscribed event triggers.

Event considerations

  • Account linking: When you link two Auth0 users, Auth0 sends a user.deleted event for the secondary account and a user.updated event for the primary account. The Action therefore deletes the secondary user in the SCIM server and updates the primary user. Unlinking restores the secondary user. To keep linked accounts in the SCIM server, restrict the synced connections with SCIM_CONNECTION_ALLOWLIST.
  • Reactivation after brute-force block: Unlike tenant administrator blocks, which send a user.updated event, brute-force protection blocks expire automatically without firing an event. The Action therefore does not update the SCIM server until the next profile change.