---
title: "Identity API: Webhook Events"
description: "The Identity API publishes webhook events when producer identity, contact, financial, or E&O data changes. Use these events to keep downstream systems synchronized without polling."
url: "https://developer.agentsync.io/identity-api-webhooks"
image: "https://developer.agentsync.io/_og/d/c_Ocean.takumi,title_~SWRlbnRpdHkgQVBJOiBXZWJob29rIEV2ZW50cw,description_~VGhlIElkZW50aXR5IEFQSSBwdWJsaXNoZXMgd2ViaG9vayBldmVudHMgd2hlbiBwcm9kdWNlciBpZGVudGl0eSwgY29udGFjdCwgZmluYW5jaWFsLCBvciBFJk8gZGF0YSBjaGFuZ2VzLiBVc2UgdGhlc2UgZXZlbnRzIHRvIGtlZXAgZG93bnN0cmVhbSBzeXN0ZW1zIHN5bmNocm9uaXplZCB3aXRob3V0IHBvbGxpbmcu,props_eyJ0aGVtZSI6eyJtb2RlIjoibGlnaHQiLCJjb2xvcnMiOnsicHJpbWFyeSI6IiMxODdFRkYifX19,p_Ii9pZGVudGl0eS1hcGktd2ViaG9va3Mi,s_CKB7e09-PoYcE7vW.png"
---

# Identity API: Webhook Events

The Identity API publishes webhook events when producer identity, contact, financial, or E&O data changes. Use these events to keep downstream systems synchronized without polling.

For instructions on how to register an endpoint, verify delivery, and validate signatures, see the [Webhooks Quick Start Guide](https://developer.agentsync.io/webhooks-quick-start-guide).

Sensitive data

Identity events can carry personally identifiable and financial information (SSN, FEIN, bank account and routing numbers). Treat every event payload as sensitive — restrict logging, encrypt at rest, and limit access to systems that need it.

## [Available Events](#available-events)

| Event Type                               | When It Fires                                                     | Common Use Cases                                                                 |
| :--------------------------------------- | :---------------------------------------------------------------- | :------------------------------------------------------------------------------- |
| id.producer.updated                      | A producer identity record (person or firm) is created or updated | Keep producer master data in sync — names, NPN, demographics                     |
| id.producer.deleted                      | A producer identity record is deleted                             | Remove the producer from downstream stores; reconcile deletions                  |
| id.producer.address.updated              | A single address record is created or updated                     | Update a single stored address record                                            |
| id.producer.address.deleted              | A single address record is deleted                                | Remove a single stored address record                                            |
| id.producer.addresses.updated            | A producer's address set changes — carries the full current set   | Replace your whole stored address set in one write, with no follow-up calls      |
| id.producer.phone.updated                | A single phone record is created or updated                       | Update a single stored phone record                                              |
| id.producer.phone.deleted                | A single phone record is deleted                                  | Remove a single stored phone record                                              |
| id.producer.phone_numbers.updated        | A producer's phone set changes — carries the full current set     | Replace your whole stored phone set in one write, with no follow-up calls        |
| id.producer.bank_account.updated         | A bank account record is created or updated                       | Keep payment details current ahead of a commission run                           |
| id.producer.bank_account.deleted         | A bank account record is deleted                                  | Stop routing payments to a removed account                                       |
| id.producer.errors_and_omissions.updated | An E&O policy record is created or updated                        | Track E&O coverage and expiry; gate appointments on active coverage              |
| id.producer.errors_and_omissions.deleted | An E&O policy record is deleted                                   | Drop the policy from your coverage records; re-check whether any active coverage remains |
| id.producer.pay_distribution.updated     | A producer's pay distribution configuration changes               | Update commission payout splits before the next pay cycle                        |
| id.producer.firm.assigned                | An individual producer is associated with a firm                  | Update firm rosters; apply firm-level entitlements                               |
| id.producer.firm.unassigned              | An individual producer is removed from a firm                     | Remove firm-level access and roster membership                                   |

## [Event Schema](#event-schema)

All Identity API webhook events share the same envelope:

```json
{
  "id": "whe_01je7babqteg8v9p30k8zpg3ga",
  "type": "id.producer.updated",
  "timestamp": "2026-06-30T17:33:00Z",
  "data": {}
}
```

| Field     | Type              | Description                                                                      |
| :-------- | :---------------- | :------------------------------------------------------------------------------- |
| id        | string            | Unique identifier for this event. Uses the whe_ prefix. Repeated on retries — use for deduplication. |
| type      | string            | The specific event type (see table above)                                        |
| timestamp | string (ISO 8601) | UTC time the event was generated                                                 |
| data      | object            | Event-specific payload (see below)                                               |

## [Field Names: Events vs. REST](#field-names-events-vs-rest)

Some early events were named independently of the REST API, so a handful of fields differ from their REST equivalents. These divergences are frozen — renaming them would break existing subscribers — and all newer events use the REST field names. Where an event field diverges, map it using this table:

| Resource        | REST field                        | Event field (legacy)                                                          | Events affected                                              |
| :-------------- | :-------------------------------- | :---------------------------------------------------------------------------- | :----------------------------------------------------------- |
| Address         | id                                | addressId                                                                     | id.producer.address.updated / .deleted                       |
| Address         | type                              | addressType                                                                   | id.producer.address.updated                                  |
| Phone           | id                                | phoneId                                                                       | id.producer.phone.updated / .deleted                         |
| Phone           | type                              | phoneType                                                                     | id.producer.phone.updated                                    |
| InsurancePolicy | id                                | policyId                                                                      | id.producer.errors_and_omissions.updated / .deleted          |
| InsurancePolicy | type                              | policyType                                                                    | id.producer.errors_and_omissions.updated                     |
| InsurancePolicy | carrier                           | providerName                                                                  | id.producer.errors_and_omissions.updated                     |
| InsurancePolicy | expiry                            | expirationDate                                                                | id.producer.errors_and_omissions.updated                     |
| Firm            | name                              | businessName                                                                  | id.producer.updated                                          |
| Firm            | type                              | businessType                                                                  | id.producer.updated                                          |
| Firm            | email                             | primaryEmail                                                                  | id.producer.updated                                          |
| BankAccount     | id                                | bankAccountId                                                                 | id.producer.bank_account.updated / .deleted                  |
| BankAccount     | name, number, routingNumber, type | bankAccountName, bankAccountNumber, bankAccountRoutingNumber, bankAccountType | id.producer.pay_distribution.updated (embedded bank account) |
| PayDistribution | id                                | payDistributionId                                                             | id.producer.pay_distribution.updated                         |

Events also carry context fields (`producerId`, `npn`) that have no REST-resource equivalent — these identify the owning producer and are not divergences.

## [Event Payloads](#event-payloads)

### [`id.producer.updated`](#idproducerupdated)

Fires when a producer identity record is created or updated. The shape of `data` differs between individual producers and firms.

| Field                                   | Type      | Applies to                         |
| :-------------------------------------- | :-------- | :--------------------------------- |
| id                                      | UUID      | Both                               |
| firmId                                  | UUID      | Individuals associated with a firm |
| npn                                     | String    | Both                               |
| type                                    | String    | Both (INDIVIDUAL or FIRM)          |
| firstName, middleName, lastName, suffix | String    | Individual only                    |
| preferredFirstName                      | String    | Individual only                    |
| gender                                  | String    | Individual only                    |
| dateOfBirth                             | Date      | Individual only                    |
| ssn                                     | String    | Individual only                    |
| primaryEmail, secondaryEmail            | String    | Individual only                    |
| finraCrdNumber                          | String    | Both                               |
| businessName                            | String    | Firm only (REST: name)             |
| businessType                            | String    | Firm only (REST: type)             |
| fein                                    | String    | Firm only                          |
| createdAt, updatedAt                    | Date-Time | Both                               |

**Individual example:**

```json
{
  "id": "whe_01je7babqteg8v9p30k8zpg3ga",
  "type": "id.producer.updated",
  "data": {
    "id": "18de9286-2ad9-4b83-96c7-6d4d63a085b1",
    "type": "INDIVIDUAL",
    "npn": "444444",
    "firstName": "Richard",
    "middleName": "Person",
    "lastName": "Rogers",
    "suffix": "",
    "preferredFirstName": "Rich",
    "gender": "MALE",
    "dateOfBirth": "1987-01-01",
    "ssn": "123456788",
    "primaryEmail": "richard@example.com",
    "secondaryEmail": "",
    "finraCrdNumber": "2523523",
    "createdAt": "2026-01-15T10:30:00Z",
    "updatedAt": "2026-06-30T17:33:00Z"
  }
}
```

**Firm example:**

```json
{
  "id": "whe_01je7babqteg8v9p30k8zpg3ga",
  "type": "id.producer.updated",
  "data": {
    "id": "18de9286-2ad9-4b83-96c7-6d4d63a085b0",
    "type": "FIRM",
    "npn": "222222",
    "businessName": "RDRR LLC",
    "businessType": "LLC",
    "fein": "123456789",
    "primaryEmail": "rdrr@example.com",
    "finraCrdNumber": "",
    "createdAt": "2026-01-15T10:30:00Z",
    "updatedAt": "2026-06-30T17:33:00Z"
  }
}
```

---

### [`id.producer.deleted`](#idproducerdeleted)

| Field | Type   | Description              |
| :---- | :----- | :----------------------- |
| id    | UUID   | The producer's record ID |
| npn   | String | The producer's NPN       |

```json
{
  "id": "whe_01je7babqteg8v9p30k8zpg3ga",
  "type": "id.producer.deleted",
  "data": {
    "id": "18de9286-2ad9-4b83-96c7-6d4d63a085b0",
    "npn": "222222"
  }
}
```

---

### [`id.producer.address.updated`](#idproduceraddressupdated)

Fires when a single address record is created or updated.

| Field                             | Type      | Description                                         |
| :-------------------------------- | :-------- | :-------------------------------------------------- |
| producerId                        | UUID      | The producer's record ID                            |
| npn                               | String    | The producer's NPN                                  |
| addressId                         | UUID      | Unique identifier for the address record (REST: id) |
| addressType                       | String    | Type of address, e.g. MAILING (REST: type)          |
| addressLine1, addressLine2        | String    | Street address                                      |
| city, county, state, zip, country | String    | Location fields                                     |
| moveInDate                        | Date      | Date the producer moved to this address             |
| preferred                         | Boolean   | Whether this is the preferred address               |
| updatedAt                         | Date-Time | When the record was last updated                    |

```json
{
  "id": "whe_01je7babqteg8v9p30k8zpg3ga",
  "type": "id.producer.address.updated",
  "data": {
    "producerId": "be941609-884a-466b-8a87-2f158d79ebec",
    "npn": "1234",
    "addressId": "5025c204-5c07-47c6-b9d3-11ad09c35f43",
    "addressType": "MAILING",
    "addressLine1": "123 Easy Street",
    "addressLine2": "Suite 500",
    "city": "Athens",
    "county": "Clarke",
    "state": "GA",
    "zip": "30606",
    "country": "USA",
    "moveInDate": "2026-05-02",
    "preferred": true,
    "updatedAt": "2026-06-05T00:03:37Z"
  }
}
```

### [`id.producer.address.deleted`](#idproduceraddressdeleted)

| Field      | Type   | Description                               |
| :--------- | :----- | :---------------------------------------- |
| producerId | UUID   | The producer's record ID                  |
| npn        | String | The producer's NPN                        |
| addressId  | UUID   | Unique identifier for the deleted address |

---

### [`id.producer.addresses.updated`](#idproduceraddressesupdated)

Fires when a producer's address set changes and carries the **full current set** of addresses, so you can replace your stored state without follow-up calls. `producerId` may be a person or firm ID.

| Field      | Type      | Description                                                                      |
| :--------- | :-------- | :------------------------------------------------------------------------------- |
| producerId | UUID      | The producer's record ID (person or firm)                                        |
| npn        | String    | The producer's NPN                                                               |
| addresses  | Array     | The producer's current addresses — each with id, type, addressLine1, addressLine2, city, state, zip, county, country, moveInDate, preferred |
| updatedAt  | Date-Time | When this snapshot was generated                                                 |

---

### [`id.producer.phone.updated`](#idproducerphoneupdated)

| Field      | Type      | Description                                       |
| :--------- | :-------- | :------------------------------------------------ |
| producerId | UUID      | The producer's record ID                          |
| npn        | String    | The producer's NPN                                |
| phoneId    | UUID      | Unique identifier for the phone record (REST: id) |
| number     | String    | The phone number                                  |
| phoneType  | String    | Type of phone number, e.g. CELL (REST: type)      |
| preferred  | Boolean   | Whether this is the preferred phone number        |
| updatedAt  | Date-Time | When the record was last updated                  |

### [`id.producer.phone.deleted`](#idproducerphonedeleted)

| Field      | Type   | Description                                    |
| :--------- | :----- | :--------------------------------------------- |
| producerId | UUID   | The producer's record ID                       |
| npn        | String | The producer's NPN                             |
| phoneId    | UUID   | Unique identifier for the deleted phone record |

---

### [`id.producer.phone_numbers.updated`](#idproducerphone_numbersupdated)

Fires when a producer's phone set changes and carries the **full current set** of phone numbers. `producerId` may be a person or firm ID.

| Field        | Type      | Description                                                                      |
| :----------- | :-------- | :------------------------------------------------------------------------------- |
| producerId   | UUID      | The producer's record ID (person or firm)                                        |
| npn          | String    | The producer's NPN                                                               |
| phoneNumbers | Array     | The producer's current phones — each with id, number, extension, type, preferred |
| updatedAt    | Date-Time | When this snapshot was generated                                                 |

---

### [`id.producer.bank_account.updated`](#idproducerbank_accountupdated)

Fires when a bank account record is created or updated.

| Field            | Type      | Description                                                          |
| :--------------- | :-------- | :------------------------------------------------------------------- |
| producerId       | UUID      | The producer's record ID                                             |
| npn              | String    | The producer's NPN                                                   |
| bankAccountId    | UUID      | Unique identifier for the bank account record (REST: id)             |
| type             | String    | Account type (e.g. CHECKING)                                         |
| number           | String    | The bank account number                                              |
| routingNumber    | String    | Bank routing number                                                  |
| name             | String    | Display label for the bank account                                   |
| primaryAccount   | Boolean   | Whether this is the producer's primary account                       |
| payDistributions | Array     | Pay distribution configurations associated with the producer, if any |
| updatedAt        | Date-Time | When the record was last updated                                     |

> **Handle with care:** this payload contains bank account and routing numbers. Do not write it to general-purpose logs, and store it only in systems approved for financial data.

```json
{
  "id": "whe_01je7babqteg8v9p30k8zpg3ga",
  "type": "id.producer.bank_account.updated",
  "data": {
    "producerId": "00283595-fc7b-4999-80ab-fb4a70c21b04",
    "npn": "1234",
    "bankAccountId": "5025c204-5c07-47c6-b9d3-11ad09c35f43",
    "type": "CHECKING",
    "number": "1234567890",
    "routingNumber": "123456789",
    "name": "Wells Fargo",
    "primaryAccount": true,
    "updatedAt": "2026-06-05T00:03:37Z"
  }
}
```

### [`id.producer.bank_account.deleted`](#idproducerbank_accountdeleted)

| Field         | Type   | Description                                    |
| :------------ | :----- | :--------------------------------------------- |
| producerId    | UUID   | The producer's record ID                       |
| npn           | String | The producer's NPN                             |
| bankAccountId | UUID   | Unique identifier for the deleted bank account |

---

### [`id.producer.errors_and_omissions.updated`](#idproducererrors_and_omissionsupdated)

Fires when an errors and omissions (E&O) policy record is created or updated.

| Field             | Type      | Description                                                |
| :---------------- | :-------- | :--------------------------------------------------------- |
| producerId        | UUID      | The producer's record ID                                   |
| npn               | String    | The producer's NPN                                         |
| policyId          | UUID      | Unique identifier for the E&O policy (REST: id)            |
| policyType        | String    | Type of policy, e.g. INDIVIDUAL (REST: type)               |
| policyNumber      | String    | Policy number                                              |
| carrier           | String    | Name of the E&O insurance provider                         |
| providerName      | String    | Legacy duplicate of carrier — prefer carrier               |
| totalLimit        | Decimal   | Total coverage limit                                       |
| effectiveDate     | Date      | When the policy became effective                           |
| expirationDate    | Date      | When the policy expires (REST: expiry)                     |
| certificateNumber | String    | Certificate number                                         |
| hasFile           | Boolean   | Whether a certificate document is uploaded for this policy |
| updatedAt         | Date-Time | When the record was last updated                           |

```json
{
  "id": "whe_01je7babqteg8v9p30k8zpg3ga",
  "type": "id.producer.errors_and_omissions.updated",
  "data": {
    "producerId": "fcd7c256-2cdc-43ad-b4ce-6c52c0e1cf20",
    "npn": "2523523",
    "policyId": "c2e7c7ac-c994-4989-a703-7eec29a9038b",
    "policyType": "INDIVIDUAL",
    "policyNumber": "EO-458812",
    "carrier": "EZLandC",
    "providerName": "EZLandC",
    "totalLimit": 1000000,
    "effectiveDate": "2026-01-04",
    "expirationDate": "2027-01-04",
    "certificateNumber": "4524524",
    "hasFile": true,
    "updatedAt": "2026-06-05T00:03:37Z"
  }
}
```

### [`id.producer.errors_and_omissions.deleted`](#idproducererrors_and_omissionsdeleted)

| Field      | Type   | Description                                  |
| :--------- | :----- | :------------------------------------------- |
| producerId | UUID   | The producer's record ID                     |
| npn        | String | The producer's NPN                           |
| policyId   | UUID   | Unique identifier for the deleted E&O policy |

---

### [`id.producer.pay_distribution.updated`](#idproducerpay_distributionupdated)

Fires when a producer's pay distribution configuration changes. Each distribution splits commission payments across one or more bank accounts.

| Field             | Type   | Description                                                                      |
| :---------------- | :----- | :------------------------------------------------------------------------------- |
| payDistributionId | String | Unique identifier for the pay distribution (REST: id)                            |
| producerId        | UUID   | The producer's record ID                                                         |
| producerType      | String | person or firm                                                                   |
| effectiveFrom     | Date   | Start of the distribution's effective window (inclusive)                         |
| effectiveUntil    | Date   | End of the effective window (exclusive)                                          |
| details           | Array  | Distribution splits — each with id, bankAccountId, bankAccountName, bankAccountNumber, bankAccountRoutingNumber, bankAccountType, primaryAccount, percentage, amount, priority, remainder |

> **Handle with care:** the embedded bank account details include account and routing numbers.

---

### [`id.producer.firm.assigned`](#idproducerfirmassigned)

Fires when an individual producer is associated with a firm.

| Field       | Type   | Description                           |
| :---------- | :----- | :------------------------------------ |
| producerId  | UUID   | The individual producer's record ID   |
| producerNpn | String | The individual producer's NPN         |
| firmId      | UUID   | The firm the producer was assigned to |
| firmNpn     | String | The firm's NPN                        |

### [`id.producer.firm.unassigned`](#idproducerfirmunassigned)

Fires when an individual producer is removed from a firm. Carries the same payload shape as `id.producer.firm.assigned`.

---

## [Subscribing to Events](#subscribing-to-events)

When registering your endpoint in the webhook portal, subscribe to the `id` high-level event group to receive all Identity API events, or subscribe to specific event types if you only need a subset.

See the [Webhooks Quick Start Guide](https://developer.agentsync.io/webhooks-quick-start-guide) for step-by-step registration instructions.

## [Processing Recommendations](#processing-recommendations)

-   **Use `id` for deduplication** — if an event is retried, the envelope `id` stays the same. Store processed event IDs to avoid duplicate processing.
-   **Respond quickly** — your endpoint must return `2xx` within 5 seconds. Offload heavy processing to a queue or background job.
-   **Validate signatures** — verify the `webhook-signature` header against your signing secret before processing.
-   **Restrict logging of payloads** — bank account and identity events carry sensitive data; log the envelope (`id`, `type`, `timestamp`) rather than the full `data` object.
-   **Use `updatedAt` for ordering** — if you receive multiple events for the same resource in quick succession, use `data.updatedAt` to determine the most recent state.
-   **Reconcile with REST** — webhook delivery is at-least-once, not guaranteed-ordered. Periodically reconcile against `GET /v2/persons` and `GET /v2/bank-accounts` with `updated_since` as a safety net.