API reference / Applications

post/v1/applications

Create an application on behalf of a traveller

Operation
createApplication
Authentication
API key

Creates a draft. Scoped to the authenticated tenant. Keys issued to one tenant can never read or modify another tenant’s resources.

The price is snapshotted onto the application at creation, so a later government fee change cannot re-price work already in flight.

Request

Body

product_codestringrequired
nationality_iso3stringrequired

example: SOM

destination_iso3stringrequired

one of: ETHSOMDJIKEN

example: KEN

localestring

one of: enarsoframswdeitestrzhja

travellersobject[]

Everyone travelling with the applicant, excluding the applicant. Empty or omitted means a single-traveller application. At most 6 people in total, which is the Kenyan eTA’s own limit.

service_tierstring

Service tier. Omitted means "guided". The fee is charged once per order, not per traveller.

one of: guidedpriorityexpress

example: guided

Responses

201Created.
idstringrequired
referencestringrequired

Safe to read aloud.

example: HV-QYFHW6M4D8

product_idstringrequired
nationality_iso3stringrequired
destination_iso3stringrequired

one of: ETHSOMDJIKEN

statusstringrequired

one of: draftawaiting_paymentpaidsubmittedunder_reviewapprovedrejectedcancelledexpired

form_dataobjectrequired

The applicant’s own answers, keyed by the wizard’s field paths: `personal.*`, `passport.*`, `contact.*`, `travel.*`, `accommodation.*`, `purpose.*`, `host.*`, `family.*`, `employment.*`, `emergency.*`, plus a `documents` array. **Partial by design** — the wizard autosaves the whole object as the applicant types, so a draft holds only the fields filled in so far, and a `PATCH` merges rather than replaces. Which fields apply depends on the destination and on earlier answers. Unknown keys are accepted and ignored; `extensions` is the map that is validated strictly.

extensionsobjectrequired

Answers the destination’s own form asks for and ours does not. Validated against the captured flow for this destination — an unrecognised key is refused, because a field the government will read is not a place to accept a typo. See `packages/reference`.

travellersobject[]required

Everyone travelling with the applicant — the applicant is not in this list. Empty for a single-traveller application, which is every application created before groups existed. Set at creation and frozen with the price, because the party size is what the price is for.

pricing_snapshotobjectrequired

The price agreed at creation, frozen. A later government fee change must never re-price an in-flight application, and a dispute months later has to be answerable from this row. `travellers` in here is the party size **including the lead**, and the fees are per person — the total is their product.

versionintegerrequired

Optimistic concurrency token. Send the version you last saw on a PATCH.

government_referencestringnullrequired
submitted_atstringnullrequired
decided_atstringnullrequired
created_atstringrequired
updated_atstringrequired
400The request body or query failed validation.
errorobjectrequired
401No key, an unknown key, a revoked key, or a key whose tenant is not active. All four return an identical body on purpose: distinguishing them would let a caller discover which partners we have.
errorobjectrequired
403The key is valid but does not hold the scope this route requires.
errorobjectrequired
404Not found, or not yours. A resource belonging to another tenant answers 404 rather than 403, because 403 would confirm the record exists.
errorobjectrequired

Example

curl
curl -X POST https://api.haavisa.com/v1/applications \
  -H "Authorization: Bearer $HAAVISA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "product_code": "<product_code>",
    "nationality_iso3": "SOM",
    "destination_iso3": "KEN"
  }'

More in Applications

Something unclear? The eligibility guide explains the model behind the answers; the errors guide covers the envelope.