truvo.md
If you’re a coding agent, this file is for you. It explains what the Truvo API does, how to authenticate, every operation, the sandbox, and the rules for errors, retries, polling, and request keys. Each section links to the guide with more detail. Every example uses the sandbox. Sandbox quotes are synthetic, and a sandbox key never reads a live record or causes a live effect.
What Truvo does
The Truvo API gets insurance quotes from inside your product. You send one applicant’s facts for one line, Truvo gets the quotes in the background, and you read the result when it’s ready.
- Lines:
personal_auto,renters, andpet. One quote request covers one line. - Pet is quote plus a hand-off: the customer buys on the carrier’s site, and Truvo doesn’t learn the result.
- Purchase: the API doesn’t have the purchase operations yet.
Terms
Keys and environments
Send your key as a bearer token on every request:
- Both environments use the base URL
https://api.truvo.com/v1/, and the key prefix picks the environment:trv_sandbox_issandbox, andtrv_live_islive. After the prefix, a key has 32 letters and digits. - Truvo reads the environment and the customer from the key. Don’t send
environment,customer, orcustomer_idin the query or the body, or anX-Environment,X-Customer, orX-Customer-Idheader, because the API refuses that with422invalid_input. - The sandbox works before your organization is approved. Live traffic needs Truvo’s approval.
- A key proves which integration is calling. It doesn’t prove authority over a particular customer, so Truvo checks the two separately. A resource that doesn’t exist and one outside your authority get the same
404. - A missing or invalid key gets
401authentication_failed. An outage never returns401, so treat a401as a problem with the key. - Every answer, success or error, carries a
Request-Idheader. An error body repeats it asrequest_id.
More: Keys and environments.
Operations
Every path sits under https://api.truvo.com. The OpenAPI document for these operations is https://api.truvo.com/v1/openapi.json, and the API reference shows the same schemas.
A cancel answers cancelled, or cannot_stop when the work already went out and can’t stop.
The feedback, webhook, event feed, and request log operations answer 404, the same as an unknown path, while Truvo has them turned off. The webhook and event feed routes aren’t in the OpenAPI document yet.
Get a quote
These four steps price one personal auto quote in the sandbox. Set your key first:
1. Check what your key can do
The answer gives the line, the environment, the states, a JSON Schema of the facts the line requires, and the action types a quote can offer. Build the application from that schema, and ask the person for each fact it needs. Don’t fill in a fact with a guess.
2. Start a quote request
Send the facts for one line, with a new request key. Every value in this body is synthetic:
consent_receipt_idis the ID of the applicant’s consent for Truvo to get quotes for them. It covers quoting only. Consent receipts lists the other receipts.- For auto, driver 1 is the named insured, and the API takes the applicant’s date of birth from driver 1.
- Money values are integers in minor units, so
10000000is $100,000.
The API answers 202 with a quote request in the queued status. Save its id.
This body names no scenario, so in a new sandbox workspace it runs auto-one-driver-one-vehicle and returns one quote. Sandbox shows how to pick another outcome.
3. Wait for the result
Read it again while the status is queued or running. Polling has a loop that does this.
4. Read a quote
Once the status is completed, quotes gives the ID and the version of each quote. Read one:
Without version, you get the latest version. Add ?version= to read a specific one.
Consent receipts
Each receipt is the ID of the person’s own record of one choice, and it covers only that choice. Never send one receipt ID in two fields, and never make a receipt up. Truvo reads IDs that differ only in case or separators, or that add segments to another ID on either side, as one receipt: Consent_1, consent-1.contact, and x:consent-1:y all match consent-1.
contact_consent holds the receipt ID and the channels the person agreed to, each listed once. Set a channel in contact_channels to true only if the receipt lists it. A channel you leave out stays false.
A mismatch gets 422 invalid_input with a field error:
The quote request
queued and running mean the work is still going. Every other status is final, so stop reading when you see one.
completenessispending,partial, orcomplete. A partial result can still be useful.quotesgives the ID and the version of each quote.market_outcomesgives one outcome for each carrier:quote,decline,missing_information,failed,unknown, orno_answer. Aquoteoutcome names itsquote_idandquote_version.requirementslists what’s still needed. Each entry has akind(missing_factorinteraction), apath, and acode.next_actionslists what you can do next.failureisnullunless the request failed. Then it says why:no_quotes_by_deadlineorquote_run_not_started. Its retry action isuse_new_request_key, so send the request again with a newIdempotency-Key.
A completed request with zero quotes isn’t a failure: a decline or an answer with no offers still completes. Check market_outcomes to see how each carrier answered.
Truvo stops collecting answers 60 seconds after it accepts a quote request. A carrier that hasn’t answered by then reads no_answer, and an answer that arrives later never reopens the request.
More: Get quote results.
The quote
The next action types are read_status, continue_on_carrier_site, prepare_purchase, open_hosted_checkout, and request_assistance. A quote lists only the ones it supports. Two of them work like this:
read_statusnames a resource to read again.continue_on_carrier_sitegives aurlto open in a new tab. The link goes through a Truvo redirect to the carrier’s site and works untilavailability.expires_at. After that, it opens a Truvo page that says the quote has expired. In the sandbox, the link opens a Truvo sandbox page, never a carrier site.
An expired quote lists only read_status. Send a new quote request for a new price.
Rules for showing quotes to a person
- Offer only the actions a quote lists. Never infer an action from the line or the carrier.
- A quote is a price offer, not coverage. A checkout link isn’t proof of payment or coverage either.
- One carrier’s quote isn’t a market comparison.
- An unknown value stays unknown. Don’t fill in a price, a fee, or a limit that the quote doesn’t give.
- A sandbox quote has
evidence.labelmock, no comparison amount, and the coverage conditionSandbox premium. Not a carrier price.
Polling
A quote request runs in the background, so read it until its status is final. A read never starts a new rating run.
- Add
Prefer: wait=Nto hold the read open for up toNseconds, whereNis a whole number from 0 to 20. The API answers as soon as the status is final, or when the time runs out. Without the header, it answers at once. - When the server held the read open, the answer carries
Preference-Applied: wait=N. - When the server can’t hold the read, it answers at once without
Preference-Appliedand withRetry-After. Wait that many seconds before you read again. - A
Prefervalue that isn’t valid gets422invalid_input. - Over MCP,
get_quote_requesttakeswait_seconds, from 0 to 20, in place of the header.
This loop waits for the Retry-After seconds when the answer has that header, and for 1 second when the server didn’t hold the read open:
Request keys
Networks drop responses. If that happens after you send a create, you can’t tell whether Truvo accepted it. A request key fixes that: send the same request with the same key, and Truvo returns the original result instead of starting a second request.
POST /v1/quote-requests,POST /v1/event-subscriptions,POST /v1/event-subscriptions/{id}/rotate,POST /v1/event-subscriptions/{id}/test,POST /v1/event-subscriptions/{id}/replay, and the three sandbox commands (POST /v1/sandbox/scenarios,POST /v1/sandbox/reset, andPOST /v1/sandbox/controls) require theIdempotency-Keyheader. Reads don’t take one.- A key is 1 to 255 characters long, made of letters, digits,
.,_,:, and-. Use a new key for each new request. A UUID works well. - The same key with the same body returns the original result. Truvo checks your current authority first, and it doesn’t start a second request.
- The same key with a different body gets
409conflict, with the retry actionuse_new_request_key. In the sandbox, a changedscenariocounts as a different body. - A key’s scope is one environment, one integration, one resource, and one operation.
- Over MCP,
request_keycarries the same key, with the same replay rules.
A key replays for at least 24 hours.
More: Retry safely with request keys.
Errors
When a request fails, the body tells you what went wrong, which fields caused it, and what to do about it. Branch your code on error.code and error.retry_action.type. The message is written for a person.
field_errors is a list of fields, each with a path and a message, or null. doc_url has the form https://docs.truvo.com/v1/errors/<code>, and it opens the code table in the Errors guide.
Only the purchase operations return quote_expired, price_changed, and purchase_not_supported. The API doesn’t have those operations yet.
When to retry
- A timeout or a network error. Send the same body with the same request key.
503service_unavailable. Wait for the time inRetry-After, then send the same body with the same key.500internal_error. Send the same body with the same key.429rate_limited. Wait for the time inRetry-After, then send the request again.409conflict. Your body is different from the first request with this key. To make a new request, use a new key.- A quote request that ends
failed. Send it again with a new key.
Every operation except capabilities reports its rate limit in the RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers. They describe the limit you’re closest to reaching. RateLimit-Reset is the seconds until its count resets. Most windows are fixed at one minute, and a burst of up to twice the limit across a window edge is possible.
The sandbox also has daily limits, which reset at 00:00 UTC. Each organization can send 5,000 quote requests a day, and each call that counts toward its quote request limit counts toward this one too, even one that fails validation. When all organizations together have 25,000 sandbox quote requests accepted in a day, the sandbox is busy: until 00:00 UTC, it accepts new quote requests only from organizations with fewer than 500 accepted that day, up to 50,000 in all. A request that fails validation doesn’t count toward the 25,000 and 50,000 totals. A 429 from a daily limit says Try again after 00:00 UTC., and Retry-After gives the seconds until then.
Truvo can suspend an organization’s sandbox, for example when its traffic looks like abuse. Then each of its sandbox keys gets 403 forbidden with the retry action contact_support, and it can’t create sandbox keys, until Truvo restores it. Its live keys keep working.
More: Errors.
Webhooks
Instead of polling, subscribe a URL to the event types you want, and Truvo sends a signed POST each time one happens. The Webhooks guide has the details and the list of event types.
- Subscribe. Send
POST /v1/event-subscriptionswith anIdempotency-Key, anhttpsurlon a public host, and the exactevent_typesyou want. There’s no wildcard. The answer shows thesigning_secretonce, so store it right away. Only a server key can manage subscriptions, and each integration can hold 10 active subscriptions in each environment.PATCHandDELETEon/v1/event-subscriptions/{id}change or disable one, andPOST /v1/event-subscriptions/{id}/rotatereplaces the secret. The old secret keeps working for 24 hours. - Verify. Each delivery carries the
svix-id,svix-timestamp, andsvix-signatureheaders. The signature is an HMAC-SHA256 over{svix-id}.{svix-timestamp}.{body}, keyed with the part of your signing secret afterwhsec_, decoded from base64. Verify the raw body, before any JSON parsing. Thesvixlibraries accept these headers as they are, and theirverifyrefuses a timestamp more than 5 minutes from your clock. - Retries. Answer
2xxwithin 15 seconds. Any other answer counts as a failure, and the message is sent again after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours, and 10 hours. If every delivery fails for 5 days, delivery turns off until you sendPATCHwith your URL. The same event can arrive more than once and out of order, so skip anidyou’ve already processed. - Thin events. An event names the resource that changed, in
resource.type,resource.id, andresource.version, and it carries no customer data. Read the resource to see what changed, and never apply an olderresource.versionover a newer one. Ignore an event type or a field you don’t know, and still answer2xx. - Resync through the feed.
GET /v1/eventsholds every event of the last 30 days, with the sameidas its webhook. Page through it withcursorandlimit(10 by default, 100 at most), store eachnext_cursoronly after your work is saved, and stop at an empty page. Afterevent_cursor_expired, read each resource you track, then read the feed again without a cursor.
To check your endpoint, send a test event with POST /v1/event-subscriptions/{id}/test and a new Idempotency-Key. To see a real delivery in the sandbox, start a quote request or run the force_expiry control.
Sandbox
Each sandbox key has its own sandbox workspace, with an active scenario, its own clock, and your sandbox records. The paths under /v1/sandbox/ work only with a sandbox key: a live key gets 404, and a sandbox key without authority gets 403.
A scenario decides what a sandbox quote request returns. Pick one in either of two ways:
- For one request: add
"scenario": "<id>"to the body ofPOST /v1/quote-requests, or thescenarioargument over MCP. The scenario must be for the request’s line. A live request that names a scenario gets422invalid_input. - For the workspace:
POST /v1/sandbox/scenarioswith{"scenario": "<id>"}and anIdempotency-Key. The scenario applies to each later quote request of its line, until you select another one or reset the workspace. Requests for other lines keep their default.
A new workspace has no scenario selected, so each line runs its default and returns one quote: auto runs auto-one-driver-one-vehicle, renters runs renters-one-offer, and pet runs pet-one-dog. The default sandbox scenario prices whatever pets, drivers, or vehicles you send. GET /v1/sandbox/scenarios lists the whole catalog, with each scenario’s line, summary, status, and completeness.
The catalog also has purchase and payment scenarios for auto and renters, but the API doesn’t have the purchase operations yet.
Sandbox controls go to POST /v1/sandbox/controls, one at a time, each with its own Idempotency-Key:
POST /v1/sandbox/reset with the body {} and an Idempotency-Key clears the workspace’s records, and puts the controls and every line’s scenario back to their start values. GET /v1/sandbox/workspace returns active_scenario, clock_offset_seconds, workspace_time, quotes_expired_at, reset_count, and scenario_catalog_version.
More: Sandbox scenarios.
MCP
The Truvo MCP server at https://api.truvo.com/v1/mcp serves the quote operations as tools, over Streamable HTTP, with the same key as a bearer token.
The server lists the five webhook tools only while webhooks are on. A refused call comes back as a tool error that carries the same error envelope as HTTP. Canceling a quote request and the sandbox controls have no tool, so use HTTP for those.
More: Agent setup, with the config for Claude Code, Cursor, Codex, and VS Code.
Feedback
If the API is missing something, an answer is wrong, or an error or a page is unclear, tell Truvo. POST /v1/feedback takes a JSON body:
kind:missing_feature,bug,unclear_error,unclear_docs, orother. Required.summary: one line, up to 200 characters, that says what went wrong or what’s missing. Required.expectedandactual: what you expected, and what happened instead, up to 2,000 characters each. Required.tried: theoperationor MCP tool you called, and therequest_idsof up to 20 calls that show the problem.agent: your agent’sname, such as Claude Code, and itsversion.
The API answers 201 with a feedback_id that starts with fb_ and the status received. GET /v1/feedback/{id} returns the report’s status (received, planned, shipped, or declined) and an optional note from Truvo. Leave out personal data: the API refuses text that looks like a Social Security, driver’s license, or card number.
Links
- Quickstart: the same first quote, step by step.
- Agent setup: connect an MCP client, and an
AGENTS.mdblock for your repo. - Webhooks: subscriptions, signatures, retries, event types, and the event feed.
- API reference: every operation and its schemas.
/llms.txt: an index of every docs page. Add.mdto any page URL to get its Markdown.