> ## Documentation Index
> Fetch the complete documentation index at: https://docs.turrisfi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agency License Expiring

> Webhook event triggered 60 days before an agency license reaches its renewal date

## Overview

The `DOWNSTREAM_ENTITY_LICENSE_EXPIRING` webhook is triggered when one of your agencies' licenses is **60 days** from its renewal date.

It exists because a lapsing agency license did not reliably reach you. [Agency Compliance Status Change](/guides/webhooks/downstream-entity-compliance-status-change) fires per product and state, and only when a compliance status actually flips, so a license expiring in a state you have no requirement for is silent. Nothing announced the approaching date itself.

**Webhook type:** `DOWNSTREAM_ENTITY_LICENSE_EXPIRING`

### Triggers

One delivery per license, on the day it is exactly 60 days from its `nextRenewalDate`.

<Info>
  Deliveries land on a daily sweep at **05:00 America/New\_York**, not in real time. This is a date crossing rather
  than a change to a record.
</Info>

<Warning>
  **The 60 days here is not the 30 days in the Turris web app.** The app's own "Expiring Soon" badge appears at 30
  days, so between 60 and 30 days out you will hold a license this webhook called expiring that the screen does not.

  Both numbers are correct for their purpose and neither is changing. Every delivery carries `daysUntilExpiration`
  **and** `expiringWindowDays` so the two surfaces reconcile. Read them rather than inferring a threshold.
</Warning>

## What does not fire this event

| Change                                       | Use instead                                                                                                                                                                                     |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The license passes its renewal date          | [Agency License Expired](/guides/webhooks/downstream-entity-license-expired)                                                                                                                    |
| A license with **no** `nextRenewalDate`      | Nothing. Perpetual and non-expiring licenses never fire either event                                                                                                                            |
| A license whose `status` is `inactive`       | Nothing. NIPR has already flagged it                                                                                                                                                            |
| A license revoked or suspended for cause     | [Agency Regulatory Action Added](/guides/webhooks/downstream-entity-regulatory-action-added), or [Agency Compliance Status Change](/guides/webhooks/downstream-entity-compliance-status-change) |
| A producer license reaching its renewal date | [Agent License Expiring](/guides/webhooks/agent-license-expiring)                                                                                                                               |
| Appointments, or product/state compliance    | [Agency Compliance Status Change](/guides/webhooks/downstream-entity-compliance-status-change)                                                                                                  |

### Licenses marked do-not-renew DO fire

If a license has been marked do-not-renew, this event **still fires**. Turris suppresses its own renewal reminders in that case, because there is no point nagging about a license someone decided to drop.

For a carrier the reasoning inverts: an agency deliberately letting a license lapse is one of the most actionable things you can hear about your distribution book.

### Perpetual licenses never fire, and there are more than you might think

Both events are driven entirely by a range query on `nextRenewalDate`. A license without one cannot match, so it never produces a delivery.

That is **roughly 11% of agency licenses** on the platform. If you reconcile deliveries against your own license count you will find a gap, and this is it. Read [Get Downstream Entity Licenses](/api-reference/v2/licenses/downstream-entity-licenses) for the full picture.

### There is no "expired" license status to key on

`status` is copied verbatim from NIPR and nothing in Turris derives it from a date, so a license 60 days from expiry and one that lapsed last week both normally carry `status: "active"`.

Use `daysUntilExpiration` and `nextRenewalDate`. Do not use `status` to infer expiry.

## Payload Example

```json theme={null} theme={null}
{
  "webhookType": "DOWNSTREAM_ENTITY_LICENSE_EXPIRING",
  "upstreamEntityId": "507f1f77bcf86cd799439010",
  "payload": {
    "licenseId": "507f1f77bcf86cd799439021",
    "niprDataSubscriptionId": "507f1f77bcf86cd799439014",
    "npn": "7654321",
    "stateCode": "FL",
    "licenseNumber": "L098765",
    "licenseClassCode": "PC",
    "licenseClassName": "Property & Casualty",
    "status": "active",
    "residencyStatus": "Resident",
    "issueDate": "2018-06-15T00:00:00.000Z",
    "nextRenewalDate": "2026-10-19T00:00:00.000Z",
    "linesOfAuthority": [
      { "code": "PROP", "name": "Property", "status": "Active" },
      { "code": "SURP", "name": "Surplus Lines", "status": "Active" }
    ],
    "daysUntilExpiration": 60,
    "expiringWindowDays": 60,
    "downstreamEntityId": "507f1f77bcf86cd799439012",
    "downstreamEntityAssociationId": "507f1f77bcf86cd799439011",
    "legalName": "RT Specialty LLC",
    "branchName": "RT Specialty - Miami Beach, FL",
    "producerCode": "03-e55f06b3-618c-4bf2-b578-efae34a87a6e"
  }
}
```

### Payload Fields

| Field                           | Type                          | Description                                                                                      |
| ------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------ |
| `licenseId`                     | string                        | Turris's id for this license record.                                                             |
| `niprDataSubscriptionId`        | string                        | The NIPR data subscription this license was retrieved under.                                     |
| `npn`                           | string                        | The agency's National Producer Number.                                                           |
| `stateCode`                     | string                        | State that issued the license, e.g. `FL`.                                                        |
| `licenseNumber`                 | string                        | The license number as filed with the state.                                                      |
| `licenseClassCode`              | string                        | License class code, e.g. `PC`.                                                                   |
| `licenseClassName`              | string                        | Human-readable class.                                                                            |
| `status`                        | string                        | Verbatim NIPR status, `active` or `inactive`. **Never reflects expiry** — see above.             |
| `residencyStatus`               | string                        | Resident or non-resident.                                                                        |
| `issueDate`                     | string \| null                | ISO-8601.                                                                                        |
| `nextRenewalDate`               | string                        | ISO-8601. Always present: a license without one cannot fire this event.                          |
| `linesOfAuthority`              | array                         | `code`, `name` and `status` per line of authority.                                               |
| `daysUntilExpiration`           | number                        | Whole days to `nextRenewalDate`. Positive on this event.                                         |
| `expiringWindowDays`            | number                        | The threshold that triggered the delivery, currently `60`. Read this rather than hard-coding it. |
| `downstreamEntityId`            | string, may be absent         | The agency record. Shared by every relationship you hold with it.                                |
| `downstreamEntityAssociationId` | string, may be absent         | The relationship you hold with that agency. **Match on this.**                                   |
| `legalName`                     | string, may be absent         | The agency's legal name.                                                                         |
| `branchName`                    | string, may be absent         | Branch name of this relationship.                                                                |
| `producerCode`                  | string \| null, may be absent | Producer code assigned for this relationship.                                                    |

<Note>
  **The entity fields are best-effort, and they all depend on one lookup.** `downstreamEntityId`, `legalName`,
  `downstreamEntityAssociationId`, `branchName` and `producerCode` are resolved from your association with the
  agency. If that lookup misses — an agency archived between our read and the delivery is the realistic case —
  **none of them arrive**. If it hits, `downstreamEntityId` and `downstreamEntityAssociationId` are always
  present and the rest are individually best-effort.

  One distinction worth reading carefully: an **absent** `producerCode` key means the lookup did not resolve,
  whereas `producerCode: null` means it resolved and no producer code is assigned.
</Note>

<Note>
  There is deliberately **no `expirationDate`** field. `nextRenewalDate` is the only expiry date the platform holds.
  (The license GET reference currently documents an `expirationDate`; that field does not exist on any record and no
  response has ever carried a value for it.)
</Note>

## Deduplication and volume

Each license matches the 60-day boundary on exactly **one** calendar day, so you receive one delivery per license per renewal cycle. No debouncing, no batching. A renewal that moves `nextRenewalDate` forward re-arms the event for the new date rather than cancelling anything already sent.

<Warning>
  **Size your receiver for the spike, not for the median.** The two are three orders of magnitude apart. A typical
  sweep delivers **single digits**, and the 95th percentile is still under **50** — but the busiest measured sweep for
  a single carrier carries about **1,850 deliveries of this event alone**.

  Return `2xx` quickly and queue the work rather than processing inline. A receiver that handles the median sweep
  comfortably can still fall over on a peak one.
</Warning>

<Warning>
  If the daily sweep does not run, that day's licenses are **not** announced later. There is no catch-up pass. If you
  need a guarantee rather than a notification, reconcile against
  [Get Downstream Entity Licenses](/api-reference/v2/licenses/downstream-entity-licenses) on a schedule.
</Warning>

<Warning>
  Webhook subscriptions are per event type. Subscribing to Agency License Expiring does **not** deliver
  [Agency License Expired](/guides/webhooks/downstream-entity-license-expired) — register a separate webhook for each.
</Warning>


## Related topics

- [Agent License Expiring](/guides/webhooks/agent-license-expiring.md)
- [Agency License Expired](/guides/webhooks/downstream-entity-license-expired.md)
- [Changelog](/changelog.md)
