Skip to content

Availability and Quoting

Before creating an order you need a package (ctgId) and a price. The fastest way to get both is to request a quote — it resolves the correct package for the route, traveller age, and altitude, and returns the exact amount payable.

The catalogue endpoints are there for browsing and for building your own selection UI.

Quote v1 is deprecated — move to v2 by 31 December 2026

POST /api/external/v1/reference/quote (quote from ages) is deprecated and will stop working after 31 December 2026. Use POST /api/external/v2/reference/quote, which takes the traveller's date of birth instead of an age.

Until then v1 keeps working unchanged, and every v1 response carries these headers so you can detect it in code:

Deprecation: true
Sunset: Thu, 31 Dec 2026 23:59:59 GMT
Link: </api/external/v2/reference/quote>; rel="successor-version"

See Migrating from v1.


POST /v2/reference/quote

Prices a trip for one traveller from their date of birth and returns the recommended package.

Authentication: required. Use the same bearer token as every other endpoint.

This is the only v2 endpoint

The path is /api/external/v2/reference/quote. Every other endpoint stays under /api/external/v1.

One traveller per quote

A quote covers exactly one person. dobs must contain exactly one date. To price a group, request one quote per traveller and sum the results.

Request

POST /api/external/v2/reference/quote
Authorization: Bearer <accessToken>
Content-Type: application/json
Field Type Required Validation
dobs Date[] Yes Exactly one date of birth, YYYY-MM-DD, in the past
nationality String Yes Non-blank, max 100 characters
startDate Date Yes First day of cover, YYYY-MM-DD
durationDays Integer Yes Days of the trip, counting startDate as day 1. Positive
routeId UUID No A known route. See GET /reference/routes
maxAltitude Integer Conditional Required when routeId is omitted. 0–9000 metres

Passing routeId is preferred: it resolves the altitude for you and returns a route name you can show the customer.

{
  "nationality": "American",
  "dobs": ["1998-03-14"],
  "routeId": "550e8400-e29b-41d4-a716-446655440000",
  "startDate": "2026-10-01",
  "durationDays": 14
}

How the age is worked out

You send the date of birth; HGN works out the age. The traveller is aged on the last day of the policy, because that is the oldest they will be while insured and the package has to cover that age.

Package Last day of the policy
Special route startDate + durationDays − 1 (the trip)
Any other package startDate + package days − 1. A package can cover more days than the trip, for example a 10-day trek on a 14-day package is insured for 14 days

Ages are whole years and never rounded up: 17 years and 11 months is 17. A traveller who has a birthday before the last day of the policy is quoted at the new age, and may get a different package.

The response tells you exactly what was used: endDate is the last insured day and agesUsed the age the price is for.

Response

200 OK

{
  "success": true,
  "message": "Quote generated successfully",
  "data": {
    "quoteId": "11111111-1111-1111-1111-111111111111",
    "quoteStatus": "QUOTED",
    "createdAt": "2026-07-07T03:30:00Z",
    "ctgId": "8d9f0e5a-1234-4f2e-bc3a-6b1a2c4d5e6f",
    "ctgCode": "CTG/TOURIST/MIDDLE",
    "ctgName": "Tourist – Middle Altitude",
    "nationality": "American",
    "numberOfPersons": 1,
    "ages": [28],
    "agesUsed": [28],
    "routeId": "550e8400-e29b-41d4-a716-446655440000",
    "routeName": "Annapurna Base Camp",
    "maxAltitude": 4130,
    "startDate": "2026-10-01",
    "endDate": "2026-10-14",
    "durationDays": 14,
    "totalAmount": 590.00,
    "insuranceCompany": "Example Insurance",
    "coverage": [
      { "code": "LIB_DEATH", "name": "Accidental Death and Disability", "coverageAmount": 20000.00 },
      { "code": "LIB_MEDICAL", "name": "Accidental Medical Treatment", "coverageAmount": 3500.00 },
      { "code": "LIB_TRANSPORT", "name": "Emergency Medical Transportation", "coverageAmount": 8000.00 },
      { "code": "LIB_REPATRIATE", "name": "Repatriation of Corpse", "coverageAmount": 2000.00 }
    ],
    "includedDevices": [
      {
        "deviceId": "a1b2c3d4-5678-4abc-9def-0123456789ab",
        "name": "Tracer M3",
        "description": "CTG tracer device with emergency SOS for high-altitude routes",
        "quantity": 1,
        "priceForDuration": 40.00
      }
    ],
    "deviceSelectionPricing": {
      "hasIncludedMandatoryDevice": true,
      "devicePriceImpact": 51.00
    }
  },
  "timestamp": "2026-07-07T03:30:00Z"
}

The fields are the same as v1 (see the field table below), with two differences:

Field v2 meaning
endDate Last insured day, inclusive. In v1 it was startDate + duration, the day after
agesUsed New. The age the quote is priced for, taken on endDate

The order checks the same age

When you create the order, HGN ages the traveller on the order's last covered day and checks the package covers that age. If it does not, for example because you changed the departure date, the order is rejected with 400 and you need a new quote.

Dropping an included device

includedDevices are bundled into totalAmount. To exclude one, pass its deviceId in excludedIncludedDeviceIds when you create the order.

The order total without the device is totalAmount − deviceSelectionPricing.devicePriceImpact. Do not subtract priceForDuration: device fees also carry markup and VAT, so the real drop is larger.

Examples

curl -X POST https://apitest.himalayanguardian.com/api/external/v2/reference/quote \
  -H "Authorization: Bearer $HGN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "nationality": "American",
    "dobs": ["1998-03-14"],
    "routeId": "550e8400-e29b-41d4-a716-446655440000",
    "startDate": "2026-10-01",
    "durationDays": 14
  }'
const res = await fetch("https://apitest.himalayanguardian.com/api/external/v2/reference/quote", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${await getAccessToken()}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    nationality: "American",
    dobs: ["1998-03-14"],
    routeId: "550e8400-e29b-41d4-a716-446655440000",
    startDate: "2026-10-01",
    durationDays: 14,
  }),
});

const { data: quote } = await res.json();
console.log(quote.ctgName, quote.totalAmount, quote.endDate, quote.agesUsed);
res = requests.post(
    "https://apitest.himalayanguardian.com/api/external/v2/reference/quote",
    headers={"Authorization": f"Bearer {access_token}"},
    json={
        "nationality": "American",
        "dobs": ["1998-03-14"],
        "routeId": "550e8400-e29b-41d4-a716-446655440000",
        "startDate": "2026-10-01",
        "durationDays": 14,
    },
    timeout=10,
)
res.raise_for_status()

quote = res.json()["data"]
print(quote["ctgName"], quote["totalAmount"], quote["endDate"], quote["agesUsed"])

Errors

Status Cause
400 Validation failure — no date of birth or more than one, a date of birth not in the past, missing or non-positive durationDays, missing both routeId and maxAltitude, or no package covers the traveller for the whole policy
401 Missing or invalid token
404 routeId does not match a known route

Migrating from v1

v1 request v2 request
POST /api/external/v1/reference/quote POST /api/external/v2/reference/quote
ages: [28] dobs: ["1998-03-14"]
duration: 14 or endDate durationDays: 14
numberOfPersons removed

Nothing else changes: same token, same quoteId/ctgId to echo into POST /orders, same catalogue and routes. In the response, read endDate as the last insured day and use agesUsed if you show the age.


POST /reference/quote (deprecated)

Deprecated — stops working after 31 December 2026

Use POST /v2/reference/quote. See Migrating from v1.

Prices a trip from an age and returns the recommended package.

Authentication: required.

One traveller per quote

A quote covers exactly one person. ages must contain exactly one value. To price a group, request one quote per traveller and sum the results.

Request

POST /api/external/v1/reference/quote
Authorization: Bearer <accessToken>
Content-Type: application/json
Field Type Required Validation
nationality String Yes Non-blank, max 100 characters
ages Integer[] Yes Exactly one age, each 1–120
startDate Date Yes YYYY-MM-DD
endDate Date Conditional Required if duration is omitted. Must be after startDate
duration Integer Conditional Required if endDate is omitted. Positive integer, in days. If you send both, duration must equal endDate − startDate
routeId UUID No A known route. See GET /reference/routes
maxAltitude Integer Conditional Required when routeId is omitted. 0–9000 metres
numberOfPersons Integer No Deprecated. If sent, must be 1. Omit it

Trip length: send endDate or duration (both is allowed only if they agree). For a special route you may omit both; the route's minimum duration is used.

Altitude: send routeId or maxAltitude. Passing routeId is preferred: it resolves the altitude for you and returns a route name you can show the customer.

{
  "nationality": "American",
  "ages": [28],
  "routeId": "550e8400-e29b-41d4-a716-446655440000",
  "startDate": "2026-10-01",
  "duration": 14
}

Response

200 OK

{
  "success": true,
  "message": "Quote generated successfully",
  "data": {
    "quoteId": "11111111-1111-1111-1111-111111111111",
    "quoteStatus": "QUOTED",
    "createdAt": "2026-07-07T03:30:00Z",
    "ctgId": "8d9f0e5a-1234-4f2e-bc3a-6b1a2c4d5e6f",
    "ctgCode": "CTG/TOURIST/MIDDLE",
    "ctgName": "Tourist – Middle Altitude",
    "nationality": "American",
    "numberOfPersons": 1,
    "ages": [28],
    "routeId": "550e8400-e29b-41d4-a716-446655440000",
    "routeName": "Annapurna Base Camp",
    "maxAltitude": 4130,
    "startDate": "2026-10-01",
    "endDate": "2026-10-15",
    "durationDays": 14,
    "totalAmount": 590.00,
    "insuranceCompany": "Example Insurance",
    "coverage": [
      { "code": "LIB_DEATH", "name": "Accidental Death and Disability", "coverageAmount": 20000.00 },
      { "code": "LIB_MEDICAL", "name": "Accidental Medical Treatment", "coverageAmount": 3500.00 },
      { "code": "LIB_TRANSPORT", "name": "Emergency Medical Transportation", "coverageAmount": 8000.00 },
      { "code": "LIB_REPATRIATE", "name": "Repatriation of Corpse", "coverageAmount": 2000.00 }
    ],
    "includedDevices": [
      {
        "deviceId": "a1b2c3d4-5678-4abc-9def-0123456789ab",
        "name": "Tracer M3",
        "description": "CTG tracer device with emergency SOS for high-altitude routes",
        "quantity": 1,
        "priceForDuration": 40.00
      }
    ],
    "deviceSelectionPricing": {
      "hasIncludedMandatoryDevice": true,
      "devicePriceImpact": 51.00
    }
  },
  "timestamp": "2026-07-07T03:30:00Z"
}
Field Type Description
quoteId UUID Pass back as quoteId when creating the order
quoteStatus String QUOTED for a new quote
createdAt Timestamp When the quote was generated
ctgId UUID Recommended package. Pass back as ctgId when creating the order
ctgCode String Package display code
ctgName String Package display name
nationality String Echoed from the request
numberOfPersons Integer Always 1
ages Integer[] Echoed from the request
routeId UUID Resolved route
routeName String Resolved route name
maxAltitude Integer Route maximum altitude in metres
startDate Date Echoed from the request
endDate Date startDate + durationDays. Sent value is echoed; if you sent only duration, it is calculated
durationDays Integer Trip length in days: endDate − startDate
totalAmount Decimal Total payable for this quote
insuranceCompany String Underwriter display name
coverage Object[] Liability coverages: code, name, coverageAmount
includedDevices Object[] Bundled devices: deviceId, name, description, quantity, priceForDuration
deviceSelectionPricing Object hasIncludedMandatoryDevice, devicePriceImpact: how much totalAmount drops if the bundled device is excluded

The quote is a complete order blueprint

You do not need a catalogue lookup between quoting and ordering. Map the quote straight onto the order:

Quote field Order field
ctgId ctgId
quoteId quoteId
routeId routeId
startDate departureDate

Examples

curl -X POST https://apitest.himalayanguardian.com/api/external/v1/reference/quote \
  -H "Authorization: Bearer $HGN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "nationality": "American",
    "ages": [28],
    "routeId": "550e8400-e29b-41d4-a716-446655440000",
    "startDate": "2026-10-01",
    "duration": 14
  }'
const res = await fetch(`${BASE_URL}/reference/quote`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${await getAccessToken()}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    nationality: "American",
    ages: [28],
    routeId: "550e8400-e29b-41d4-a716-446655440000",
    startDate: "2026-10-01",
    duration: 14,
  }),
});

const { data: quote } = await res.json();
console.log(quote.ctgName, quote.totalAmount);
res = requests.post(
    f"{BASE_URL}/reference/quote",
    headers={"Authorization": f"Bearer {access_token}"},
    json={
        "nationality": "American",
        "ages": [28],
        "routeId": "550e8400-e29b-41d4-a716-446655440000",
        "startDate": "2026-10-01",
        "duration": 14,
    },
    timeout=10,
)
res.raise_for_status()

quote = res.json()["data"]
print(quote["ctgName"], quote["totalAmount"])

Errors

Status Cause
400 Validation failure — more than one age, age out of range, missing both endDate and duration, endDate not after startDate, duration not matching the dates, missing both routeId and maxAltitude, or numberOfPersons other than 1
401 Missing or invalid token
404 routeId does not match a known route

A validation failure returns field-level detail. See Errors.


GET /reference/ctgs

Lists the active public package catalogue. Use this to build a browse-and-select UI, or to look up package detail you want to display alongside a quote.

Authentication: required.

Query parameters

Name Type Default Description
search String — Free-text search
altitudeBand String — Filter by altitude band
durationBand String — Filter by duration band
ageBand String — Filter by age band
productType String — Filter by insurance product type
page Integer 0 Zero-based page index
size Integer 20 Page size, capped at 100
sortBy String displayOrder Sort field
sortDir String asc asc or desc

Response

200 OK — data is a paginated result whose content array holds package objects.

Field Type Description
ctgId UUID Package ID for quoting and ordering
ctgCode String Display code
ctgName String Display name
insuranceProductId String Underlying insurance product ID
routeId UUID Linked route, for special-route products
insuranceCompany String Underwriter display name
minAltitude Integer Altitude band lower bound, metres
maxAltitude Integer Altitude band upper bound, metres
baseDurationDays Integer Base duration
minAge Integer Minimum eligible age
maxAge Integer Maximum eligible age
baseCtgPrice Decimal Stored base/display price
ctgPriceForDuration Decimal Price calculated for the duration
includedDevicesPrice Decimal Mandatory included-device price
totalPrice Decimal Total public listing price
liabilities Object[] Liability coverages
includedDevices Object[] Included product devices
description String Product description
termsConditions String Terms summary or link
curl -G https://apitest.himalayanguardian.com/api/external/v1/reference/ctgs \
  -H "Authorization: Bearer $HGN_TOKEN" \
  --data-urlencode "search=tourist" \
  --data-urlencode "size=50"

Listed price vs. quoted price

totalPrice here is the public listing price for the package. The amount your customer actually pays is totalAmount from POST /v2/reference/quote, which accounts for the specific traveller, route, and dates. Always charge against the quote.


GET /reference/routes

Lists active trekking routes. Only active routes are returned — there is no parameter to include inactive ones.

Authentication: required.

Query parameters

Name Type Default Description
search String — Free-text search
region String — Filter by region
trekName String — Filter by trek name
routeType String — Filter by route type
routeCategory String — Filter by route category
page Integer 0 Zero-based page index
size Integer 20 Page size, capped at 100
sortBy String name Sort field
sortDir String asc asc or desc

Response

200 OK — data is a paginated result whose content array holds route summary objects. These are the same route summaries the public booking widget uses.

The field you need for the rest of the flow is routeId, which you pass to POST /v2/reference/quote and then to POST /orders. Routes are also filterable by region, trek name, route type, and route category, as shown above.

curl -G https://apitest.himalayanguardian.com/api/external/v1/reference/routes \
  -H "Authorization: Bearer $HGN_TOKEN" \
  --data-urlencode "region=Annapurna" \
  --data-urlencode "sortBy=name"

This endpoint uses a different pagination envelope

GET /reference/routes wraps its results in a different page envelope from GET /reference/ctgs and GET /travellers. All three expose a content array; the surrounding metadata field names differ. If you share one pagination helper across endpoints, read content and avoid depending on the metadata field names.

Custom routes

If a trip does not follow a catalogued route, you can skip routeId entirely and create the order with a customRouteName instead. See Booking. Quotes still need either a routeId or a maxAltitude.


Next steps

  • Booking — register the traveller and create the order
  • Examples — the full flow end to end