Webhooks
Quote requests run in the background, so you need a way to hear when one finishes. Instead of polling, you can subscribe a URL to the event types you care about, and Truvo sends a signed POST to it each time one of them happens. Every event is also in the event feed for 30 days, so a missed delivery is never lost.
Subscribe
Create a subscription with the URL that receives events and the exact event types you want. There’s no wildcard, so list each type:
The answer is the subscription, with its signing secret:
Store the signing_secret right away. It’s shown once: after a create succeeds, a retry with the same request key returns the subscription with signing_secret: null. If you lose the secret, rotate it.
If a create answers 503, send it again with the same request key, and it finishes with the same secret. A create left unfinished for 10 minutes is dropped. A retry of its request key then gets 409 with the retry action use_new_request_key, so send the create again with a new key.
A few rules apply to every subscription:
- Only a server key can manage subscriptions. A widget key gets
403. - The URL must use
httpsand name a public host. A URL with a user name or a password, an IP address,localhost, or a.localor.internalhost gets422. A port is fine. - An unknown event type gets
422at that entry ofevent_types. - Each integration can hold 10 active subscriptions in each environment.
- A sandbox key sees sandbox events, and a live key sees live events.
To change the URL or the event types, send PATCH /v1/event-subscriptions/{id}. To stop deliveries, send DELETE /v1/event-subscriptions/{id}, which disables the subscription. GET /v1/event-subscriptions lists your subscriptions, 10 to a page by default.
Rotate the signing secret
To rotate (roll) the secret, send POST /v1/event-subscriptions/{id}/rotate with a new request key. The answer carries the new secret once, and previous_secret_expires_at says when the old secret stops working: 24 hours after the rotation. Until then, each delivery carries a signature made with each secret, so your endpoint accepts it with either one. Deploy the new secret before the old one expires. Only one rotation of a subscription can be in progress, so a second one gets 409 until the first finishes. A rotation left unfinished blocks new ones for up to 10 minutes.
Verify each delivery
Anyone can send a request to your URL, so check the signature before you act on a delivery. Truvo sends webhooks through Svix, and each delivery carries Svix’s standard signature headers:
The signature is an HMAC-SHA256 over {svix-id}.{svix-timestamp}.{body}. Its key is the part of your signing secret after whsec_, decoded from base64. Each entry in svix-signature is v1, followed by a signature in base64.
The signature follows the Standard Webhooks scheme. The svix libraries accept these headers directly. Any other Standard Webhooks library needs them renamed to webhook-id, webhook-timestamp, and webhook-signature, with the same values. The svix package on npm is the simplest:
verify throws when no signature matches, or when the timestamp is more than 5 minutes away from your clock. Answer 400 in that case and do nothing else. Verify against the raw body: a body that you parsed and serialized again won’t match.
Handle retries and duplicates
Answer with a 2xx status within 15 seconds to confirm a delivery. Any other answer, a redirect included, counts as a failure, and the message is sent again. The first delivery goes out right away. After a failure, the message is sent again after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours, and 10 hours.
If every delivery to your URL fails for 5 days, delivery to that subscription turns off. The subscription still reads active, and its health reads disabled. To turn delivery back on, send PATCH /v1/event-subscriptions/{id} with your URL, then recover the missed events from the feed.
Because of retries, the same event can arrive more than once, and events can arrive out of order. Handle both:
- Store the
idof each event you processed, and skip anidyou’ve already seen. - Answer
2xxfirst, and do the work afterward. A slow handler turns into retries. - Compare
resource.versionwith the version you hold. Read the resource only when the event’s version is higher, and never apply an older version over a newer one.
An event never repeats an insurance action. Reading a resource because of an event is always safe.
Read the resource
Events are thin on purpose. An event says which resource changed and which version it reached, and it carries no applicant data:
To see what changed, read the resource. A quote_request event points to GET /v1/quote-requests/{id}, and a quote event points to GET /v1/quotes/{id}. The read gives you the current state, which may already be newer than the event.
Truvo can add event types and fields without notice. Ignore a type or a field that you don’t know, and still answer 2xx.
Event types
Purchases aren’t in the API yet, so the five purchase types don’t fire yet. A pet quote request sends only quote_request events.
Recover missed events
The event feed holds every event of the last 30 days, of every type, whether or not you subscribed to it. Each event has the same id in the feed as in a webhook, so you can use one handler for both. Read the feed after an outage, after you turn a subscription back on, or on a schedule as a safety net:
The answer has events and a next_cursor. Every page has a next_cursor, an empty one included. Process the events, store the next_cursor only after that work is saved, and send it on the next read. When a page comes back empty, you’ve caught up.
- Leave out
cursorto start at the oldest event the feed still holds. limitis 10 by default and 100 at most.- A cursor that the feed didn’t give you gets
400with the codeinvalid_cursor. - A cursor gets
410with the codeevent_cursor_expiredwhen events after it are gone: the feed deleted them after 30 days, or a sandbox reset removed them. Read each resource that you track, then read the feed again without a cursor.
Test your endpoint
Send a test event to check that your endpoint gets deliveries and verifies their signatures:
The answer is 202, with the event that Truvo queued:
A test event goes only to that subscription, whatever event types it lists. It names no resource and carries no applicant data, and it isn’t in the event feed. Answer it with 2xx, as you answer any event. To retry after an error, send the same request key: it queues the same event, with the same id.
A test can take up to 35 seconds to answer, so set your client’s timeout to 40 seconds or more. If it answers 503, wait for the Retry-After seconds and send it again with the same request key.
A test to a disabled subscription gets 409. So does a test while delivery to the subscription is off: check its health.
See each delivery
GET /v1/event-subscriptions/{id}/deliveries lists the events sent to a subscription in the last 30 days, newest first. The log shows every delivery sent to this subscription, test events included:
created_at is when Truvo sent the event to delivery. limit is 10 by default and 100 at most. Send the next_cursor of a page as cursor to read the next page. A page can come back empty with a next_cursor, for example after a sandbox reset deletes events, so keep reading until next_cursor is null. A cursor that this subscription’s log didn’t give you gets 400 with the code invalid_cursor.
To see the attempts of one delivery, read GET /v1/event-subscriptions/{id}/deliveries/{event_id}. The answer is the same delivery, with up to 20 attempts, newest first:
response_status_code is null when your endpoint gave no HTTP answer, as after a timeout. trigger is manual for a test or a replay, and scheduled for the first attempt and each retry.
Replay a delivery
After you fix your endpoint, send an event from the delivery log again:
The answer is 202. The event arrives again with the same id and the same body, so your duplicate check still applies. You can replay only an event in the subscription’s delivery log. Any other event ID gets 404. Use a new request key for each replay, and the same key to retry after an error. To catch up on many events, read the event feed.
Check delivery health
GET /v1/event-subscriptions/{id}/health says whether deliveries get through:
Limits for the webhook tools
- A subscription takes 10 test events a minute, and each event can be replayed 5 times a day. Only a test or a replay that Truvo hands to delivery counts.
- An integration can send 60 tests and replays a minute in all.
- Tests and replays don’t use your quote request allowance, so they never slow down your quotes.
- The delivery log, delivery, and health reads use the quote read allowance: one unit for a page of the delivery log, and up to two for a health read or a read of one delivery. The checks before a test use one unit, and the checks before a replay use two.
- A
429carriesRetry-After. So does a503: send the same request again after that many seconds.
An agent can use the same tools over MCP: send_test_event, list_event_deliveries, get_event_delivery, replay_event_delivery, and get_event_subscription_health.
Try it in the sandbox
Send a test event first. Then make a real change with your sandbox key: start a quote request, or run the force_expiry control to expire your sandbox quotes. Each change sends its events to your subscriptions and adds them to the feed.