> ## 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.

# Changelog

> Product updates and announcements for the Turris Public API

<Update label="June 2026" description="New endpoints: list and retrieve agency contacts" tags={["New"]}>
  Two new endpoints let you read the agency contacts across your downstream entity associations:

  * `GET /v1/contacts` returns every contact you can see, one entry per contact. The agencies you can see them at are grouped under `associations`, each carrying the business roles the contact holds there and whether they are a primary contact. Both agency-wide default contacts and market-specific contacts are included. Filter by `downstreamEntityAssociationId` (a single agency relationship), `businessRole`, or both.
  * `GET /v1/contacts/{contactId}` returns a single contact by id, including every agency relationship you can see them at and the roles they hold at each. It returns `404` if the contact is not visible to your organization.

  See the [List Contacts](/api-reference/v1/contacts/list-contacts) and [Get a Contact](/api-reference/v1/contacts/get-contact) reference pages for full request and response details.
</Update>

<Update label="May 2026" description="PRODUCER_AGREEMENT_EXECUTED webhook now carries ContractContainer identifiers" tags={["Improved"]}>
  The `PRODUCER_AGREEMENT_EXECUTED` webhook payload now includes four new optional fields tying the executed agreement back to its ContractContainer instance and template:

  | Field                           | Type    | Use                                                                                                   |
  | ------------------------------- | ------- | ----------------------------------------------------------------------------------------------------- |
  | `contractContainerInstanceId`   | string? | Stable ID of the executed contract instance                                                           |
  | `contractContainerTemplateId`   | string? | Template the instance was created from (matches the `contractContainerTemplateId` you pass to invite) |
  | `contractContainerTemplateName` | string? | Human-readable template name (no extra lookup needed)                                                 |
  | `signedFileDocumentId`          | string? | FileDocument ID of the signed PDF, downloadable via the File Documents endpoint                       |

  **Action required:** None. Existing fields are unchanged. New fields are strictly additive and optional. Legacy in-flight envelopes signed before the ContractContainer migration may omit them, so treat them as optional in your handler.

  See the [Producer Agreement Executed](/guides/webhooks/producer-agreement-executed) webhook documentation for the updated payload reference.
</Update>

<Update label="May 2026" description="Contract Container Templates endpoint + invite endpoint accepts contractContainerTemplateId" tags={["New", "Improved"]}>
  **New endpoint:** `GET /v1/contract-container-templates` returns active ContractContainer templates for your organization. Use the returned `_id` as `contractContainerTemplateId` when inviting a downstream entity.

  **Invite endpoint improvement:** `POST /v1/downstream-entity-associations/invite` now accepts `contractContainerTemplateId` directly. The legacy `producerAgreementId` field is deprecated but still accepted for backward compatibility, internal resolution still maps it to the matching ContractContainer template.

  **Action required:** None. Existing integrations continue to work. For new integrations, prefer `contractContainerTemplateId` obtained from the new endpoint.
</Update>

<Update label="March 2026" description="New webhook: Producer Agreement Executed" tags={["New"]}>
  A new webhook event — `PRODUCER_AGREEMENT_EXECUTED` — is now available. It fires when a downstream entity and your upstream entity have **fully executed a producer agreement** (both parties have signed).

  **Payload fields:**

  | Field                           | Type           | Description                                                |
  | ------------------------------- | -------------- | ---------------------------------------------------------- |
  | `downstreamEntityAssociationId` | string         | The association ID                                         |
  | `downstreamEntityId`            | string         | The downstream entity (agency) that executed the agreement |
  | `producerCode`                  | string \| null | The producer code for this association                     |
  | `branchName`                    | string         | The branch name of the association                         |

  This webhook is sent **immediately** — no debouncing is applied.

  See the [Producer Agreement Executed](/guides/webhooks/producer-agreement-executed) webhook documentation for payload examples and field reference.
</Update>

<Update label="March 2026" description="Breaking change: entityId removed from Compliance Data Synchronized webhook" tags={["Breaking"]}>
  The `ENTITY_COMPLIANCE_DATA_SYNCHRONIZED` webhook payload no longer includes an `entityId` field. It has been replaced by `npn` (National Producer Number).

  **What changed:**

  | Field      | Before                                                            | After                                       |
  | ---------- | ----------------------------------------------------------------- | ------------------------------------------- |
  | `entityId` | string (MongoDB ObjectId) — was always `"undefined"` due to a bug | **Removed**                                 |
  | `npn`      | *(not present)*                                                   | string — the NPN of the synchronized entity |

  **Why NPN instead of an entity ID?** A single NPN can be linked to multiple downstream entity associations and downstream entities across your network. The NPN is the natural, universal identifier for any NIPR-driven operation and lets you look up all relevant records for that producer. Use `niprDataSubscriptionId` to retrieve the specific subscription record via the [Compliance Data Subscriptions API](/api-reference/v1/compliance-data-subscriptions/get-status).

  **Note:** The previous `entityId` field was always sent as the string `"undefined"` due to a bug — it did not contain a valid entity ID. If you were reading this field, update your handler to use `npn` instead.

  **Action required:** Update your webhook handler to read `npn` instead of `entityId`. See the [Compliance Data Synchronized](/guides/webhooks/compliance-data-synchronized) documentation for the updated payload reference.
</Update>

<Update label="March 2026" description="Validate NPNs — new endpoint" tags={["New"]}>
  Look up National Producer Numbers directly through the API. Send up to 20 NPNs in a single request and instantly find out whether each NPN belongs to an agency (firm), an individual agent, or is not recognized by NIPR — along with entity details like name, resident states, FEIN, and date of birth.

  See the [Validate NPNs endpoint](/api-reference/v1/tools/validate-npns) for full details.
</Update>

<Update label="February 2026" description="Improved error handling for concurrent duplicate requests" tags={["Improved"]}>
  When the same `Add Downstream Entity` or `Invite Downstream Entity` request is submitted multiple times simultaneously, the API now returns a clear **409 Conflict** response instead of an unhandled **500 Internal Server Error**.

  The response includes the error code `transient_transaction_error` with the message: *"This request has already been processed. A duplicate concurrent request was detected."*

  **No action required** — this is a non-breaking improvement. If you are handling `409` status codes already, you will now receive this new error code for concurrent duplicate requests.
</Update>

<Update label="February 2026" description="New payload structure for compliance status change webhooks" tags={["Breaking"]}>
  The `AGENT_COMPLIANCE_STATUS_CHANGE` and `DOWNSTREAM_ENTITY_COMPLIANCE_STATUS_CHANGE` webhook payloads now use a **hierarchical structure** grouped by entity → product → state, replacing the previous flat structure.

  **What changed:**

  | Aspect                                     | Previous                           | New                                                                       |
  | ------------------------------------------ | ---------------------------------- | ------------------------------------------------------------------------- |
  | Structure                                  | Flat — one object per state        | Nested — `products[].states[]` per entity                                 |
  | Association field                          | `upstreamDownstreamAssociationId`  | `downstreamEntityAssociationId`                                           |
  | `licenseStatus`                            | Plain string (e.g., `"Licensed"`)  | Object with `statusName` and optional `statusMessages`                    |
  | `appointmentStatus`                        | Plain string (e.g., `"Appointed"`) | Object with `statusName` and optional `statusMessages`                    |
  | `isStateRemovedFromComplianceRequirements` | Boolean field                      | Removed — state removal is now indicated by `statusName: "STATE_REMOVED"` |

  **New fields added:**

  * `legalName` — Legal name of the downstream entity
  * `branchName` — Branch name of the association
  * `productName` — Human-readable product name (alongside `productId`)
  * `statusMessages` — Optional array of detailed status explanations on `licenseStatus` and `appointmentStatus`

  **Action required:** Update your webhook handler to parse the new nested payload structure. See the [Webhooks guide](/guides/webhooks#compliance-status-change) for full payload examples and field reference.
</Update>

<Update label="February 2026" description="Compliance status change webhooks are now debounced" tags={["New"]}>
  Compliance status change webhooks (`AGENT_COMPLIANCE_STATUS_CHANGE` and `DOWNSTREAM_ENTITY_COMPLIANCE_STATUS_CHANGE`) are now debounced with a **1-minute sliding window**. When a change is detected, Turris waits for 1 minute of inactivity before sending the webhook. If additional changes occur within that window, the timer resets. A maximum cap of 30 minutes ensures delivery even during continuous activity.

  This means there is a minimum \~1 minute delay between a data change and webhook delivery. The debouncing consolidates rapid sequences of changes (e.g., PDB alert processing, bulk operations) into a single webhook with a batched payload.

  `ENTITY_COMPLIANCE_DATA_SYNCHRONIZED` webhooks are **not affected** — they are still sent immediately.

  See the [Webhooks guide](/guides/webhooks#event-debouncing--batching) for details.
</Update>

<Update label="February 2026" description="Webhook payloads are now sent as plaintext JSON with HMAC-SHA256 signatures" tags={["Breaking"]}>
  Webhook requests are no longer encrypted with AES-256-GCM. Instead, payloads are sent as **plaintext JSON** (`Content-Type: application/json`) and signed with HMAC-SHA256 using your client secret.

  Each request now includes two new headers:

  | Header               | Description                                                  |
  | -------------------- | ------------------------------------------------------------ |
  | `X-Turris-Signature` | HMAC-SHA256 hex signature computed over `{timestamp}.{body}` |
  | `X-Turris-Timestamp` | Unix timestamp (seconds) when the request was signed         |

  **Action required:** Update your webhook endpoint to verify the `X-Turris-Signature` header instead of decrypting the request body. See the [Webhooks guide](/guides/webhooks#verifying-webhook-signatures) for code examples.
</Update>

<Update label="February 2026" description="Improved error messaging for GET Agent Licenses" tags={['Improved']}>
  If you request the licenses for an `agentId` that does not exist, the API now returns an error message that explicitly
  states that an agent could not be found.
</Update>

<Update label="February 2026" description="Auto-populating niprDataSubscription" tags={['Improved']}>
  For each agent or agency (downstream entity association) we now auto-populate the `niprDataSubscription` field,
  allowing you to immediately see the compliance synchronization status.
</Update>

<Update label="January 2026" description="Get Sign-In URL to share with your contacts" tags={["New"]}>
  Call the new endpoint with a specific email to receive a sign-in URL that you can share with the contact owning that email. They can use it to sign into the Agency application to either complete the onboarding flow with your organization or manage your market relationship by updating contacts, agents, or documents.

  The response returns a `404` error if the agency or a contact with the email at the agency could not be found.
</Update>

<Update label="January 2026" description="Find agencies by email domain and email" tags={['New']}>
  You can now find your associated downstream entities by email domain or a specific email address.
</Update>

<Update label="January 2026" description="Filter Downstream Entity Associations by email domain" tags={['Improved']}>
  The `GET /downstream-entity-associations` endpoint now accepts an `emailDomain` query param. Clients can find all of
  their associated agencies by the email domain of the associated agency contacts.
</Update>

<Update label="January 2026" description="Simplified the Authentication Collection" tags={['Announcement']}>
  We have moved all requests into the parent domain, simplifying the authentication collection structure.
</Update>

<Update label="January 2026" description="Dual Authentication Support" tags={["New"]}>
  All protected API endpoints now accept either authentication method:

  | Method                  | Header                      | Format               |
  | ----------------------- | --------------------------- | -------------------- |
  | OAuth (Preferred)       | `Authorization`             | `Bearer {jwt_token}` |
  | Restricted Access Token | `x-restricted-access-token` | `{your_token}`       |

  * If only a Bearer token is provided, OAuth authentication is used
  * If only a Restricted Access Token is provided, token authentication is used
  * If both headers are present, OAuth is attempted first; if it fails, the API falls back to Restricted Access Token authentication
  * Both methods produce the same authenticated context (`upstreamEntityId` and `upstreamEntityMemberId`)
</Update>

<Update label="January 2026" description="Agency Surplus Lines Licenses" tags={['Improved']}>
  This new endpoint allows clients to retrieve all active surplus lines licenses for a downstream entity association,
  filtering by FEIN and state code.
</Update>

<Update label="December 2025" description="Agency and Agent endpoints exclude soft-deleted records" tags={['Breaking']}>
  Previously, the `GET` agency/agent endpoints returned all records, including those that are soft-deleted. Since our
  users are only interested in active records, we now exclude any records with `isDeleted: true`.
</Update>

<Update label="December 2025" description="Find Agents and Agencies by License or NPN" tags={['Improved']}>
  Agents and Downstream Entity Associations can now be found and filtered either by their NPN or by their license number
  and state code. Both filter types are mutually exclusive. If a license number is provided, a valid state code must be
  provided as well.
</Update>

<Update label="November 2025" description="Regulatory Actions" tags={['New']}>
  The new regulatory actions endpoints allow you to retrieve any regulatory actions for your downstream entity
  associations and their agents.
</Update>

<Update label="November 2025" description="Appointments" tags={['New']}>
  Retrieve all appointments for your downstream entity associations and their agents. You can filter by `stateCode` and
  `status`.
</Update>

<Update label="November 2025" description="Allow filtering by license status" tags={['Breaking']}>
  Previously, the licenses endpoint returned all active licenses. We have added the `status` filter as a query param.
  Now, this endpoint returns active, inactive, or all licenses. Future iterations may add additional status values.
</Update>

<Update label="October 2025" description="Synchronize compliance data" tags={["New"]}>
  After an agent or downstream entity has been added to the tenancy, you can call this endpoint to retrieve the latest compliance data associated with the entity's NPN. The subscription includes daily updates of all compliance data.

  This endpoint runs asynchronously — it returns a success response immediately but the sync process may be queued and/or processing for minutes. Two solutions are available:

  * **Webhook**: Subscribe to a webhook and get notified when the compliance data sync completes
  * **Polling**: Poll the compliance data subscription status endpoint until `entityInfo.status: success`
</Update>

<Update label="October 2025" description="New licenses endpoints" tags={['New']}>
  Retrieve all active licenses for an agent or a downstream entity association. Optionally, provide `stateCode` to
  filter by state.
</Update>

<Update label="October 2025" description="License compliance status" tags={['Breaking']}>
  * URL params were changed to reflect the new `downstreamEntityAssociationId` nomenclature - `stateCode` query string
    is now optional; in absence of this param, the endpoint returns the compliance status for all states
</Update>

<Update label="October 2025" description="Add upload document endpoint" tags={['Improved']}>
  Users can now upload any document and associate it with any downstream entity via the downstream entity association
  ID.
</Update>

<Update label="October 2025" description="E&O/Cyber Compliance" tags={['Breaking']}>
  The URL path and query strings for the E\&O/Cyber compliance endpoint have changed.
</Update>

<Update label="October 2025" description="Top-level downstream-entity-association response" tags={["Breaking"]}>
  The `GET` and `POST` endpoints for the Downstream Entity Associations resource now return the association data alongside the downstream entity detail. The `_id` field is the `downstreamEntityAssociationId` and should be thought of as the main identifier for any association with a downstream entity.

  **What changed:**

  * URL path and query strings have changed
  * The response body now includes the association data that describes the relationship between the Downstream and the Upstream Entity
</Update>

<Update label="October 2025" description="Top-level agent model response" tags={["Breaking"]}>
  The `GET` and `POST` endpoints for the Agent resource now return the agent detail as well as the top-level association resource. Information describing the relationship between two resources is returned as sibling fields of the `agent` property.

  **What changed:**

  * URL path and query strings have changed
  * The response body now includes the association data that describes the relationship between the Agent and the Upstream Entity
</Update>

<Update label="October 2025" description="Invite agencies with custom producer agreement fields" tags={['New']}>
  If you have created a template producer agreement in the web app with custom fields (e.g. an agency-specific
  commission number), you can now assign those values when sending a request to the Invite Downstream Entity endpoint.
</Update>

<Update label="October 2025" description="Add convenience endpoint for Invite Downstream Entity" tags={['Improved']}>
  The Invite Downstream Entity endpoint now accepts an optional `downstreamEntityId` field. If provided, the endpoint
  creates a new downstream entity association with the existing downstream entity. If not provided, it creates both a
  new downstream entity and a new association.
</Update>

<Update label="October 2025" description="Invite downstream entities" tags={['New']}>
  This endpoint allows you to invite downstream entities to your organization. It creates a new downstream entity and
  association. The downstream entity receives an email invitation to complete the onboarding process.
</Update>

<Update label="September 2025" description="Webhooks" tags={["New"]}>
  Subscribe to webhooks to receive real-time notifications when events occur in your organization. Available events include:

  * Downstream entity association created/updated
  * Agent created/updated
  * Compliance data sync completed
  * Document uploaded
  * Contract signed
</Update>

<Update label="September 2025" description="Document Management" tags={['New']}>
  Upload, retrieve, and manage documents associated with downstream entities. Documents can be categorized by type
  (e.g., E\&O policy, cyber policy, W-9) and are automatically linked to the appropriate downstream entity association.
</Update>

<Update label="September 2025" description="Enhanced Agent Management" tags={["Improved"]}>
  Agents can now be added to downstream entity associations with assignment status tracking. The API supports:

  * Adding agents to agencies
  * Updating agent assignment status (pending, approved, rejected)
  * Tracking assignment history
  * Managing agent authority by product and state
</Update>

<Update label="September 2025" description="Producer Agreement Templates" tags={['New']}>
  Create and manage producer agreement templates with custom fields. Templates can be assigned to downstream entities
  during the invitation process, with custom field values populated at invitation time.
</Update>

<Update label="September 2025" description="Compliance Dashboard Data" tags={["New"]}>
  New endpoints provide aggregated compliance data for dashboard views:

  * Overall compliance status by downstream entity
  * License expiration summaries
  * E\&O/Cyber policy status
  * Regulatory action counts
  * Appointment status summaries
</Update>

<Update label="August 2025" description="Batch Operations Support" tags={["New"]}>
  New batch endpoints allow you to perform operations on multiple entities at once:

  * Batch invite downstream entities
  * Batch add agents
  * Batch update authority
  * Batch sync compliance data

  Batch operations return a job ID that can be used to track progress and retrieve results.
</Update>
