---
title: "Itinerary decision — answers a trip, not a pair"
method: POST
path: "/api/v1/visa/decision"
tags: ["Visa data"]
---

# Itinerary decision — answers a trip, not a pair

`POST /api/v1/visa/decision`

Post a whole trip — passport, ordered stops with dates, transit stops,
passport expiry — and get a per-step decision plus the blockers that
only a full itinerary reveals:

- days **accumulated** across repeat visits to the same country
  (two 20-day stops in one country are not two independent stays);
- passport validity measured against **each arrival date**;
- a passport that expires before the last exit.

Every field states its `granularity` (`pair` or `destination`), and
every gap comes back as `status: "unknown"` **with a reason** — never as
a default value. `verdict` is `no_blocker_found` or `blocked`: it
reports what the data supports, it is not legal advice.

### Known gaps, by design

- **Transit**: a transit rule exists for 44 of 238 destinations and is
  recorded *per transited country, not per nationality* — so even where
  it exists it never decides whether this passport may transit. The
  other 158 return `status: "unknown"`. Confirm with the carrier.
- **`residence` and `purpose`** are accepted and echoed back but **not
  modelled**: the dataset has no residence dimension and covers
  short-stay tourism only. Supplying either adds an entry to `unknowns`.
- **Length of stay** is computed only where `visa_free_days` holds a
  number; where the stay depends on the permit issued, `max_stay` is
  `unknown` rather than a parsed guess.

### Billing

One request per **distinct country resolved** (stops + transit), like
`/bulk`. A 3-country trip costs 3. `meta.pairs_charged` reports exactly
what was billed. Maximum 12 itinerary steps and 12 transit countries
per call.

## Request body

- DecisionRequest
  - `passport` string, required — ISO 3166-1 alpha-3 country code.
  - `itinerary` object[], required — Ordered stops. Dates are calendar days, arrival and departure both counted.
    - `country` string, required — ISO 3166-1 alpha-3 country code.
    - `from` string, date, required
    - `to` string, date, required
  - `transit` ISO3[] — Countries transited without entering. Deduplicated, and billed like a stop.
  - `passport_expiry` string, date, nullable — Omit it and every passport-validity check is skipped and reported in `unknowns`.
  - `residence` string, nullable — Accepted and echoed back, **not modelled** — adds an entry to `unknowns`.
  - `purpose` string, nullable — Accepted and echoed back. Anything other than `tourism` adds an entry to `unknowns`.

## Response `200`

Per-step decision, blockers and unknowns.

- DecisionResponse
  - `data` object
    - `passport_country` string — ISO 3166-1 alpha-3 country code.
    - `residence` string, nullable
    - `purpose` string, nullable
    - `steps` DecisionStep[]
      - `country` string — ISO 3166-1 alpha-3 country code.
      - `from` string, date
      - `to` string, date
      - `days` integer — Inclusive of arrival and departure day.
      - `admission` 'allowed' | 'conditional' | 'refused' | 'unknown' — `conditional` covers every regime that needs a formality (`eta`, `visa_on_arrival`, `e_visa`, `visa_required`). Requirements that do not map to an outcome return `unknown` rather than a guess.
      - `regime` object
        - `status` 'known' | 'unknown'
        - `value` 'visa_free' | 'visa_required' | 'e_visa' | 'visa_on_arrival' | 'eta' | 'no_admission'
        - `granularity` 'pair'
        - `source_url` string, nullable
        - `last_verified` string, nullable
      - `max_stay` object — `status: unknown` carries a `reason`; `known` carries `days` and `basis`.
        - `status` 'known' | 'unknown'
        - `days` integer, nullable
        - `granularity` 'pair'
        - `basis` string
        - `reason` string
      - `passport_validity_months` object
        - `status` 'known' | 'unknown'
        - `months` integer, nullable
        - `granularity` 'destination'
    - `transit` object[] — `status` is `destination_rule` (airport rule, not nationality) or `unknown`.
      - `country` string — ISO 3166-1 alpha-3 country code.
      - `status` 'destination_rule' | 'unknown'
      - `granularity` 'destination'
      - `note` string
      - `hubs` object[]
    - `passport` object — `status` is `checked`, or `unknown` when `passport_expiry` was omitted.
      - `expiry` string, nullable
      - `status` 'checked' | 'unknown'
      - `reason` string
      - `latest_arrival` string, nullable
      - `valid_through_itinerary` boolean
      - `per_destination` object[]
        - `country` string — ISO 3166-1 alpha-3 country code.
        - `arrival` string, date
        - `required_months` integer
        - `granularity` 'destination'
        - `satisfied` boolean
    - `blockers` object[] — Empty when nothing was found. Never a reason to conclude the trip is cleared.
      - `type` 'no_admission' | 'admission_refused' | 'stay_exceeds_allowance' | 'passport_validity' | 'passport_expires_before_exit'
      - `country` string — ISO 3166-1 alpha-3 country code.
      - `detail` string
    - `unknowns` object[] — Every gap that affected this answer, per field and per country.
      - `field` string
      - `country` string — ISO 3166-1 alpha-3 country code.
      - `reason` string
    - `verdict` 'no_blocker_found' | 'blocked' — `no_blocker_found` means the checks that could run found nothing — not that the trip is cleared.
  - `meta` object
    - `api_version` string
    - `pairs_charged` integer
    - `max_steps` integer
    - `contract` string

## Other responses

- `400` — Invalid JSON, non-ISO3 code, non-calendar date (`2026-02-31` is rejected), `to` before `from`, or more than 12 steps. The body carries a worked `example` of a valid request.
- `401` — Missing API key, query param or session cookie.
- `403` — Invalid / inactive key, or plan below the endpoint's minimum tier.
- `404` — No visa data for that passport at all — check the ISO3 code.
- `429` — Monthly quota or burst rate exceeded.

---

[API](https://skmtc.net/orizn/apis/orizn-visa-api.md) · [All operations](https://skmtc.net/orizn/apis/orizn-visa-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/orizn/orizn-visa-api/revisions/024933fc5445/schema)
