What are Webhooks?
Webhooks allow you to receive automated notifications when important compliance events occur for your downstream entities and agents. Instead of continuously polling our API for updates, we’ll automatically send HTTP POST requests to your specified endpoint whenever license statuses change or NIPR data is updated.Quick Start
1
Set up your endpoint
Create an endpoint to receive POST requests
2
Register your webhook URL
Register your webhook URL using our API
3
Verify signatures
Verify webhook signatures using your client secret
4
Handle events
Process events based on the payload data
5
Test integration
Test your integration using our test webhook feature
Available Webhook Events
Agent Compliance Status Change
Triggered when an agent’s compliance status changes due to license, appointment, or requirement updates.
Agency Compliance Status Change
Triggered when a downstream entity’s (agency) compliance status changes due to license, appointment, or requirement
updates.
Agency Created
Triggered when an agency relationship is created in your account, with the hierarchy needed to tell whether it is
covered by a parent’s producer agreement.
Agency Deleted
Triggered when an agency relationship is removed or archived.
Agency Updated
Triggered when an agency’s name, identity, or contact details change, with the fields that changed and their
previous values.
Compliance Data Synchronized
Triggered when NIPR data synchronization completes for an agent or downstream entity.
Producer Agreement Executed
Triggered when a downstream entity and your upstream entity have fully executed a producer agreement.
Surplus Lines Filing Status Change
Triggered when a surplus lines filing’s status changes (e.g., from In Progress to Filed).
Surplus Lines Filing Action Required
Triggered when a surplus lines filing checklist item escalation newly requires customer action.
Agent Regulatory Action Added
Triggered when NIPR reports a regulatory action against one of your producers that Turris has not seen before.
Agency Regulatory Action Added
Triggered when NIPR reports a regulatory action against one of your agencies that Turris has not seen before.
Agent Regulatory Action Updated
Triggered when NIPR amends a regulatory action already on file against one of your producers, for example a
disposition escalating after the original filing.
Agency Regulatory Action Updated
Triggered when NIPR amends a regulatory action already on file against one of your agencies.
Agent License Expiring
Triggered 60 days before a producer license reaches its renewal date.
Agency License Expiring
Triggered 60 days before an agency license reaches its renewal date.
Agent License Expired
Triggered the day after a producer license passes its renewal date.
Agency License Expired
Triggered the day after an agency license passes its renewal date.
Planned Webhooks
We plan to build many more webhook events, including:- Downstream entity/agent authority status
- E&O compliance status changes
- E&O/Cyber policy renewals
Payload Envelope
All webhook payloads follow this general structure:Deliveries triggered from the Turris Web App’s Send test event feature include an additional top-level
"test": true field in this envelope. Real (production) events never include it, so you can safely route or ignore test
deliveries in your receiver.Setting Up Your Webhook Endpoint
Your webhook endpoint must:Accept POST requests with JSON payloads
Respond with 2xx status codes (200-299) for successful processing
Respond within 30 seconds to avoid timeout
Handle duplicate events gracefully (use payload content for deduplication)
Verify the
X-Turris-Signature header using your client secretExample Endpoint Response
Webhook Security
All webhook requests are signed using HMAC-SHA256 with your client secret. This allows you to verify that the request originated from Turris and that the payload has not been tampered with. Webhook requests include the following headers:
The request body is sent as plaintext JSON — no decryption is required.
Verifying Webhook Signatures
Recompute the HMAC-SHA256 signature, compare it to theX-Turris-Signature header, and reject the request unless both the signature matches and the timestamp is recent.
How the Signature is Computed
The signature is computed over the string{timestamp}.{payload}, where:
{timestamp}is the value of theX-Turris-Timestampheader{payload}is the raw JSON request body (as a string)
Verification Example (Node.js)
Usage Example
Three details in the example above are the ones integrations usually get wrong:
- Sign
{timestamp}.{rawBody}, not the body alone. - Use the raw bytes, captured before
JSON.parse. Re-serialising the parsed object changes key order and whitespace, so the signature will not match even though nothing was tampered with. - Compare with a timing-safe comparison, and check the buffer lengths first, because
timingSafeEqualthrows when they differ.
packages/tooling/scripts/crypto/verify-webhook.mjs.Event Debouncing & Batching
Compliance status change webhooks use a sliding-window debounce to prevent webhook floods during bulk operations:
How it works:
- A compliance-relevant change is detected (e.g., license created, appointment updated)
- Turris starts a 1-minute timer for the affected upstream entity
- If another change occurs within that minute, the timer resets
- When 1 minute passes with no new changes (or the 30-minute cap is reached), all accumulated changes are evaluated
- A single webhook is sent containing only the entity/product/state combinations where the compliance status actually changed
Compliance Data Synchronized (
ENTITY_COMPLIANCE_DATA_SYNCHRONIZED), Producer Agreement Executed
(PRODUCER_AGREEMENT_EXECUTED), Agency Created (DOWNSTREAM_ENTITY_CREATED), Agency Deleted
(DOWNSTREAM_ENTITY_DELETED), Agency Updated (DOWNSTREAM_ENTITY_UPDATED), AML / OFAC Check Status
Change (AML_OFAC_CHECK_STATUS_CHANGE), Surplus Lines Filing Status Change
(SURPLUS_LINES_FILING_STATUS_CHANGE), Surplus Lines Filing Action Required
(SURPLUS_LINES_FILING_ACTION_REQUIRED), Agent Regulatory Action Added
(AGENT_REGULATORY_ACTION_ADDED), and Agency Regulatory Action Added
(DOWNSTREAM_ENTITY_REGULATORY_ACTION_ADDED) webhooks are sent immediately — they are not debounced, since each is
a discrete event requiring timely customer action.The four licence-expiry events are neither debounced nor immediate. Agent License
Expiring, Agency License
Expiring, Agent License
Expired and Agency License
Expired are driven by a daily sweep at 05:00
America/New_York, because they fire on a date being crossed rather than on a record changing. There is nothing to
react to at the moment it happens.One delivery per licence per boundary, no batching. Each licence matches on exactly one calendar day, so you get one
delivery per licence per renewal cycle. If a sweep does not run, that day’s licences are not announced later: there
is no catch-up pass.If you subscribe to more than one of the four, size for the combined sweep rather than the largest event. A
typical sweep is single digits and the 95th percentile is under 50, but the busiest measured sweep for a single
carrier carries about 8,000 deliveries across all four — noticeably more than the ~2,800 worst case of any one
event.The reason is that the two boundaries are 61 days apart, not 60, so their spikes do not stay separated. Renewal dates
cluster on the last day of the month. An expired spike therefore lands on the 1st, and an expiring spike
lands 60 days before a month end, which — because 60 days is not two calendar months — also falls on the 1st for some
month pairs. When both coincide, one sweep carries a whole month-end expired cohort and a whole expiring cohort at
once.
Batched Payloads
Because changes are debounced, a single webhook delivery may contain multiple status changes in thepayload array. Each element represents a distinct entity + product + state combination that changed. Design your handler to iterate over the full array.
Reliable Delivery
Automatic Retries
If your endpoint is unavailable or returns an error, we’ll automatically retry delivery using exponential backoff:
We’ll attempt delivery up to 3 times over several hours.
Manual Resend
You can manually resend any webhook event through the Turris Web App. This will cancel any pending automatic retries to prevent duplicates.Testing Your Integration
You can test your webhook integration in the following ways:- Self-test over the API (recommended for automation): Trigger a real, signed test delivery straight from your own code, no web app needed. First call List Webhooks to find the
idof the webhook you want to exercise, then call Send Test Event with thatidand an optionalscenario. Turris enqueues a real HMAC-signed POST of a realistic sample payload to that webhook’s registered URL, shaped exactly like a production event. Omitscenarioto use the type’s default; pass a scenario key to exercise a specific case (for example a surplus lines filing status, or a compliant vs non-compliant result). This is ideal for wiring webhook checks into your CI or integration tests. - Send a test event from the web app: In the Turris Web App, open Settings > Webhooks, use the actions menu on any registered webhook, and choose Send test event. Pick a scenario and trigger it. The delivery is identical to the API self-test above.
- Sandbox environment: Configure webhooks with your development endpoint and trigger compliance changes in a sandbox environment to receive real webhook payloads. See the caveat below — this does not work for the NIPR-backed events.
- Manual resend: Use the Turris Web App to resend any previously delivered webhook event to your endpoint.
"test": true in the envelope and are flagged as test events in the delivery history, so they never mix with your real events. This helps you verify reachability, signature verification, and payload parsing before any real activity exists.
For the regulatory-action events, test both scenarios, not just the default. Pass
scenario explicitly:criminal— every field populated.administrative— all seven nullable NIPR fields arrive asnull.
Monitoring & Troubleshooting
Delivery History
In our web app you can view the status of all webhook deliveries, including:- Success/failure status
- Response times
- Number of retry attempts
- Error details
Common Issues
Best Practices
Idempotency
Use payload content (entity ID, state, product) to detect and handle duplicate deliveries gracefully.
Logging
Log all incoming webhook events for debugging and audit purposes.
Async Processing
Respond quickly (2xx status) then process the event asynchronously to avoid timeouts.
Security
Store your client secret securely and consider IP whitelisting for additional security.