# User Management Syncing via Webhooks

Keeping user information up to date across multiple systems can be challenging. With Firstup’s Webhook API, you can automatically sync user profile changes (such as role updates, name changes, or deactivations) with external HRIS platforms, Active Directory, or an employee dashboard.

## Prerequisites

Before setting up user management syncing, ensure that:

* You have a publicly accessible endpoint that can receive webhook events (e.g., your HR system’s API).
* You have API access to register webhooks via the `POST /v2/webhooks/subscriptions` endpoint.
* You have permission to fetch user details using the `GET /scim/v2/users/{userId}` endpoint if you need additional data.
* You have API access to the `POST /v2/Users/{user_id}/forget` endpoint to forget users from your system (non-SCIM compliant).
* You have the following [permission scopes](https://developers.firstup.io/authorization/authenticationandauthorization#auth-scope-list-and-definitions): `webhooks.read`, `webhooks.write `.


### Webhook Subscription Attributes

| Firstup Attribute | Description |
|  --- | --- |
| `name` | A descriptive name for your webhook. |
| `url` | The external system’s endpoint that will receive webhook notifications. |
| `events` | The event type(s) to listen for. In this case, when a user is updated or deactivated. |
| `active` | Specifies whether the webhook is active. |


### Supported Webhook Events

| Event Name | When it Fires | Typical Action |
|  --- | --- | --- |
| `user.updated` | Any SCIM user attribute change (e.g., role, email, group). | Call SCIM `GET /scim/v2/users/{user_id}` |
| `user.deactivated` | A user is disabled or marked inactive in Firstup. | Deactivate or archive that yser in your identity system. |
| `user.deleted` (*optional*) | A user is fully removed (e.g., GDPR) | Remove the user from your directory or analytics. |


## Sync User Updates

Keep user records current whenever a user changes roles, email, or attributes.

1. Subscribe to `user.updated`.



```
POST /v2/webhooks/subscriptions
Authorization: Bearer <APP_TOKEN>
Content-Type: application/json

{
  "url": "https://example.com/webhooks/user-sync",
  "active": true,
  "events": [{ "name": "user.updated", "version": "1" }]
}
```

1. Receive payload.



```
{
  "event": "user.updated",
  "timestamp": "2025-11-10T15:42:30Z",
  "data": {
    "user_id": "u_987",
    "changed_fields": ["email", "role"]
  }
}
```

1. Fetch latest SCIM record.



```
GET /scim/v2/Users/{user_id}
Authorization: Bearer <APP_TOKEN>
Accept: application/scim+json
```

Use the response up update your external directory or CRM record.

## Handling Use Deactivation or Deletion

Automatically reflect account disablement or deletion.

1. Subscribe to `user.deactivated`.



```
POST /v2/webhooks/subscriptions
{
  "url": "https://example.com/webhooks/user-sync",
  "active": true,
  "events": [{ "name": "user.deactivated", "version": "1" }]
}
```

1. Receive payload.



```
{
  "event": "user.deactivated",
  "timestamp": "2025-11-11T08:00:00Z",
  "data": { "user_id": "u_654" }
}
```

1. Verify status.


`GET /scim/v2/Users/{user_id}`

Response field `"active": false` confirms deactivation.

1. If user is permanently deleted, the `/v2/Users/{user_id}/forget` endpoint removes all records. If a webhook `user.deleted` event is emitted, delete or anonymize the user on your side as well.


## Periodic Reconcilliation

With webhooks, occasional reconciliation ensures no missed updates.

`GET /scim/v2/Users?count=100`

Compare user lists between systems nightly. Use SCIM filtering (`filter=userName eq "email@example.com"`) to resolve discrepancies.

## Troubleshooting

| Issue | Likely Cause | Fix |
|  --- | --- | --- |
| `404` on `/scim/v2/Users/{id}` | User has been forgotten or never existed | Remove or skip that user |
| No webhook deliveries | Subscription inactive or incorrect event name | Verify via `GET /v2/webhooks/subscriptions` |
| Duplicate events | Retry or timeout | Deduplicate by event ID |
| Expecting `identity.* events` | These are not public | Use `user.updated` / `user.deactivated` instead |