---
title: "Price a trip"
method: POST
path: "/api/v1/travel/quotes"
tags: ["Quotes"]
---

# Price a trip

`POST /api/v1/travel/quotes`

## Request

- Base URL: `https://atlas.hellosafe.com`
- URL: `https://atlas.hellosafe.com/api/v1/travel/quotes`
- Auth: API key in header `x-atlas-key-id` and API key in header `x-atlas-timestamp` and API key in header `x-atlas-signature`

Prices one trip against the travel catalogue and returns the priced offers, cheapest first, each with its premium, its guarantee ceilings and its policy documents. Read-only: nothing is stored, no subscription is created and no attribution happens here.

Every response also opens a quoting session: `sessionId` is a signed token identifying THIS traveller's flow, and POST /links requires it. To keep one traveller's session across a trip edit, echo the previous `sessionId` in the body — a valid one is returned unchanged, anything else silently starts a fresh session (a broken continuation never fails a pricing call). Never share one sessionId across travellers.

## Request body

- QuoteRequest
  - `trip` Trip, required
    - `intent` 'forTourism' | 'schengenArea' | 'annual' | 'studyInternship' | 'whv' | 'cruise' | 'digitalNomad' | 'expat' | 'groupTravel' | 'rentalStay' | 'mountainTrip' | 'backToHome' | 'humanitarian' | 'auPair' | 'toWork' | 'cancellation', required: Trip type. `humanitarianAuPair` is still accepted and read as `humanitarian`.
    - `startDate` string, date, required: First day of cover, YYYY-MM-DD.
    - `endDate` string, date, required: Last day of cover, YYYY-MM-DD. Never before startDate.
    - `countryResidence` string, required: ISO 3166-1 alpha-2.
    - `stateResidence` 'AL' | 'AK' | 'AZ' | 'AR' | 'CA' | 'CO' | 'CT' | 'DE' | 'DC' | 'FL' | 'GA' | 'HI' | 'ID' | 'IL' | 'IN' | 'IA' | 'KS' | 'KY' | 'LA' | 'ME' | 'MD' | 'MA' | 'MI' | 'MN' | 'MS' | 'MO' | 'MT' | 'NE' | 'NV' | 'NH' | 'NJ' | 'NM' | 'NY' | 'NC' | 'ND' | 'OH' | 'OK' | 'OR' | 'PA' | 'RI' | 'SC' | 'SD' | 'TN' | 'TX' | 'UT' | 'VT' | 'VA' | 'WA' | 'WV' | 'WI' | 'WY': US residents only: the state they live in, as its USPS code (one of the 50 states or DC, also listed by GET /meta as trip.usStates). US travel insurance is regulated state by state, so `US` alone does not identify a resident. Optional, and it prices nothing: the offers and premiums are the same with or without it. It is echoed in the priced `trip`, and POST /links stores it on the subscription. Read only when countryResidence is `US` and dropped for any other residence; for a US resident, any other value is refused with BAD_STATE_RESIDENCE. The five US territories are countries, not states: a Puerto Rico resident is countryResidence `PR` with no stateResidence, and the same goes for GU, VI, AS and MP.
    - `arrivalCountries` string[], required: ISO 3166-1 alpha-2, up to 20 destinations.
    - `travellers` object[], required: One entry per traveller, up to 50. Above roughly 10 the individual products give way to the group product, which prices flat per head.
      - `age` integer, required
    - `tripPrice` number: Insured trip cost: the total price of the trip for the whole party, not a per-traveller amount. Required when shouldCoverCancellation is true.
    - `currency` string: ISO 4217. The currency we price in, and the one tripPrice and studiesAmount are read in. Default EUR. Offers come back converted into it, using the same rate, margin and rounding as the subscription funnel and the card charge. A code we cannot convert into leaves offers in the insurer's own currency rather than failing the call: 33 codes are supported (USD, EUR, GBP, JPY, CNY, CHF, CAD, AUD, SEK, NOK, DKK, PLN, HUF, CZK, RON, BGN, RSD, ALL, MKD, BAM, BRL, MXN, MAD, ZAR, PHP, TRY, INR, HKD, SGD, AED, KRW, ISK, NZD).
    - `shouldCoverCancellation` boolean: Ask for cancellation cover. Requires tripPrice.
    - `shouldCoverExtremeSports` boolean: Ask for extreme sports cover.
    - `isAnnual` boolean: Forced true by the annual and expat trip types.
  - `sessionId` string: Optional: the sessionId from THIS traveller's previous /quotes response, to keep their session across a trip edit. A valid token is echoed back; anything else silently starts a fresh session. Never reuse one across travellers.
  - `language` 'bg' | 'cs' | 'da' | 'de' | 'el' | 'en' | 'es' | 'et' | 'fi' | 'fr' | 'hr' | 'hu' | 'is' | 'it' | 'lt' | 'lv' | 'mt' | 'nl' | 'no' | 'pl' | 'pt' | 'ro' | 'sk' | 'sl' | 'sv' | 'tr': Funnel language.

## Response `200`

Priced offers.

- QuoteResponse
  - `ok` boolean
  - `mode` 'sandbox' | 'live'
  - `sessionId` string: This traveller's quoting session, `qs_<nonce>.<hmac>`. Required by POST /links. Fresh on every response unless you echoed a valid one in.
  - `offers` Offer[]
    - `id` integer
    - `name` string, nullable
    - `plan` string, nullable
    - `insurer` object
      - `name` string, nullable
      - `logoUrl` string, uri, nullable: THE IMAGE TO DISPLAY for this offer. Since 2.0.0 this is the offer's own pictogram whenever it has one, and the insurer's logo only for an offer that carries no icon. The field name is now a misnomer and is kept deliberately: your integration already reads it, so you get the new image with no change on your side. `insurer.name` is untouched, so you can keep showing "underwritten by <insurer>". The insurer's logo for an offer that has an icon is no longer published in this response.
    - `iconUrl` string, uri: The offer's own pictogram, as an SVG served by the same renderer that draws it on hellosafe.com, so your visitor sees exactly what ours does. Takes an optional `?size=16..512` (default 128); the drawing is vector, so any size is sharp. Since 2.0.0 this is the SAME URL as `insurer.logoUrl` whenever the offer has an icon, and it is published separately so you can tell the two cases apart: `iconUrl` present means the image is our pictogram, `iconUrl` ABSENT — not null, not an empty string — means the offer has no pictogram and `logoUrl` is the insurer's own logo. Reading `logoUrl` alone can no longer tell you which you got. Treat it as optional: every brokerage offer in the live catalogue carries one today (measured 2026-09-03 across FR/US/CA/AU/GB and all 16 trip types), but an offer can be published without one.
    - `price` object
      - `amount` number: What the traveller pays. When the request carried a `currency` and conversion was possible, this is the converted amount, using the same rate, margin and rounding as the subscription funnel and the card charge. Display this value. Converting `insurerAmount` yourself will quote your customer less than they are charged.
      - `amountInCents` integer: `amount` in minor units of `currency`.
      - `currency` string: ISO 4217, uppercase. Equal to the requested `currency` when conversion happened.
      - `insurerAmount` number: The insurer's own price, before conversion. Equal to `amount` when no conversion happened. For reconciliation, not for display.
      - `insurerCurrency` string: ISO 4217, uppercase. The currency the insurer prices in.
      - `isStartingPrice` boolean: true = a from price; the exact premium is set in the funnel.
      - `period` string, nullable
    - `guaranteeCurrency` string: Currency the guarantee ceilings are expressed in.
    - `guarantees` object: Keyed by guarantee slug (see GET /api/v1/travel/meta).
    - `highlights` object
      - `included` string[]
      - `excluded` string[]
    - `documents` object
      - `cgvUrl` string, uri, nullable
      - `ipidUrl` string, uri, nullable
    - `position` integer: Rank by premium, cheapest first.
  - `unpricedCount` integer: Catalogue offers that could not be priced for this trip.
  - `redirectOnlyCount` integer: Offers that matched and were priced but are deliberately NOT returned: the catalogue sells them by redirecting the traveller to the insurer's own site, so no subscription is created, the conversion postback never fires, and the sale could not be attributed or paid to you. Normally 0, because they are now excluded before we ever see them; a non-zero value means one slipped past that exclusion and our own filter caught it. Every offer you DO get back is one you can be paid on.
  - `nearMissCount` integer: Offers that would match if a filter were relaxed.
  - `trip` Trip
    - `intent` 'forTourism' | 'schengenArea' | 'annual' | 'studyInternship' | 'whv' | 'cruise' | 'digitalNomad' | 'expat' | 'groupTravel' | 'rentalStay' | 'mountainTrip' | 'backToHome' | 'humanitarian' | 'auPair' | 'toWork' | 'cancellation', required: Trip type. `humanitarianAuPair` is still accepted and read as `humanitarian`.
    - `startDate` string, date, required: First day of cover, YYYY-MM-DD.
    - `endDate` string, date, required: Last day of cover, YYYY-MM-DD. Never before startDate.
    - `countryResidence` string, required: ISO 3166-1 alpha-2.
    - `stateResidence` 'AL' | 'AK' | 'AZ' | 'AR' | 'CA' | 'CO' | 'CT' | 'DE' | 'DC' | 'FL' | 'GA' | 'HI' | 'ID' | 'IL' | 'IN' | 'IA' | 'KS' | 'KY' | 'LA' | 'ME' | 'MD' | 'MA' | 'MI' | 'MN' | 'MS' | 'MO' | 'MT' | 'NE' | 'NV' | 'NH' | 'NJ' | 'NM' | 'NY' | 'NC' | 'ND' | 'OH' | 'OK' | 'OR' | 'PA' | 'RI' | 'SC' | 'SD' | 'TN' | 'TX' | 'UT' | 'VT' | 'VA' | 'WA' | 'WV' | 'WI' | 'WY': US residents only: the state they live in, as its USPS code (one of the 50 states or DC, also listed by GET /meta as trip.usStates). US travel insurance is regulated state by state, so `US` alone does not identify a resident. Optional, and it prices nothing: the offers and premiums are the same with or without it. It is echoed in the priced `trip`, and POST /links stores it on the subscription. Read only when countryResidence is `US` and dropped for any other residence; for a US resident, any other value is refused with BAD_STATE_RESIDENCE. The five US territories are countries, not states: a Puerto Rico resident is countryResidence `PR` with no stateResidence, and the same goes for GU, VI, AS and MP.
    - `arrivalCountries` string[], required: ISO 3166-1 alpha-2, up to 20 destinations.
    - `travellers` object[], required: One entry per traveller, up to 50. Above roughly 10 the individual products give way to the group product, which prices flat per head.
      - `age` integer, required
    - `tripPrice` number: Insured trip cost: the total price of the trip for the whole party, not a per-traveller amount. Required when shouldCoverCancellation is true.
    - `currency` string: ISO 4217. The currency we price in, and the one tripPrice and studiesAmount are read in. Default EUR. Offers come back converted into it, using the same rate, margin and rounding as the subscription funnel and the card charge. A code we cannot convert into leaves offers in the insurer's own currency rather than failing the call: 33 codes are supported (USD, EUR, GBP, JPY, CNY, CHF, CAD, AUD, SEK, NOK, DKK, PLN, HUF, CZK, RON, BGN, RSD, ALL, MKD, BAM, BRL, MXN, MAD, ZAR, PHP, TRY, INR, HKD, SGD, AED, KRW, ISK, NZD).
    - `shouldCoverCancellation` boolean: Ask for cancellation cover. Requires tripPrice.
    - `shouldCoverExtremeSports` boolean: Ask for extreme sports cover.
    - `isAnnual` boolean: Forced true by the annual and expat trip types.
  - `quote` object
    - `mode` string
    - `days` integer
    - `travellers` integer
    - `expiresAt` string, date-time
  - `meta` object
    - `apiVersion` string
    - `language` string
    - `notice` string: Plain-language caveat about the prices in this response.
    - `partnerFee` object: Present ONLY when your account charges an uplift on top of the standard handling fee. It is not a separate amount to add: every price.amount below already includes it, and the funnel charges the same figure at checkout. Absent means the standard fee, and the commission your account earns by default.
      - `feeBps` integer: Whole handling fee applied to these prices, in basis points of the insurer premium (1300 = 13%).
      - `upliftBps` integer: The part of that fee you chose to add, in basis points of the premium.
      - `commissionBps` integer: What a sale at these prices pays you, in basis points of the premium.

## Other responses

- `400`: Malformed request.
- `401`: Missing, expired or invalid signature.
- `403`: The key does not carry the required scope.
- `429`: Daily quota exhausted; resets at the next UTC midnight.
- `502`: Upstream pricing failed.
- `504`: Upstream pricing timed out.

---

[API](https://skmtc.dev/hellosafe/apis/hellosafe-travel-insurance-api.md) · [All operations](https://skmtc.dev/hellosafe/apis/hellosafe-travel-insurance-api/llms.txt) · [OpenAPI document](https://skmtc.dev/hellosafe/apis/hellosafe-travel-insurance-api/revisions/8185e81c9817?raw)
