API reference / Applications

patch/v1/applications/{id}

Autosave a draft

Operation
updateApplication
Authentication
API key

Section-scoped merge. Send only what changed — the wizard autosaves every 30 seconds and a full rewrite would race the applicant's next keystroke.

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

Requires the `version` you last saw. A stale version returns **409** with the server’s current state so the client can reconcile instead of silently clobbering another tab.

Request

Parameters

idpathrequiredstring

Body

travellersobject[]

Replaces the whole travelling party, excluding the applicant. Send the full list, not a change — two devices adding a traveller each are a list, not a diff.

form_dataobject

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.

extensionsobject

Answers the destination’s own form asks for. Validated strictly against the captured flow for this destination: an unrecognised key is a 400 rather than a silent drop, because a field a government will read is not a place to accept a typo.

versionintegerrequired

Responses

200Updated, with the new version.
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
409Version conflict, or the application is no longer a draft. The body carries BOTH the standard error envelope and `current`, the server’s state — so a client can reconcile instead of discarding the other tab’s answers. It is not an `Application` on its own.
errorobjectrequired
currentApplicationrequired

Example

curl
curl -X PATCH https://api.haavisa.com/v1/applications/<id> \
  -H "Authorization: Bearer $HAAVISA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "version": 0
  }'

More in Applications

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