Embeddable widget
A requirement check your travellers can use without leaving your site. Two lines of HTML and a publishable key.
<script src="https://sdk.haavisa.com/v1/embed.js"></script>
<div id="haavisa-widget"></div>
<script>
Haavisa.mount('#haavisa-widget', {
publishableKey: 'hv_pub_live_xxxxxxxx_secret',
destination: 'KEN',
nationality: 'SOM'
});
</script>Why a shadow root
The widget renders into a shadow root, so your CSS cannot reach in and ours cannot leak out. This is not a nicety — a partner’s stylesheet with a broad selector will break any widget it can reach, and the failure looks like our bug.
/* Your page. The widget is unaffected. */
* { font-family: "Comic Sans MS" !important; color: magenta !important; }
button { display: none !important; }
select { width: 40px !important; }Options
| Option | Type | Notes |
|---|---|---|
publishableKey | string | Required. Must start with hv_pub_. |
destination | ISO3 | Required. ETH, SOM, DJI or KEN. Fixed per instance. |
nationality | ISO3 | Pre-fills the passport selector. |
baseUrl | string | Override the API host. Useful for staging. |
locale | string | Reserved. Not yet implemented. |
theme | object | primaryColor, textColor, surfaceColor, borderColor, radius, font. |
Haavisa.mount('#haavisa-widget', {
publishableKey: 'hv_pub_live_xxxxxxxx_secret',
destination: 'KEN',
theme: {
primaryColor: '#0f172a',
radius: '10px',
font: 'inherit'
}
});What it will not do
- It never says “no visa needed” unless it knows. Where we hold no verified rule it says so — the alternative is reassuring someone who is then turned away at a gate.
- It cannot create applications. A publishable key cannot write. The visitor is checking, not booking.
- It does not render API internals. Eligibility notes are written for an airline’s integration; the widget summarises them for a traveller.
- It offers a short nationality list, not all 249. A real limitation. If your audience is outside those twenty countries, embed the API directly rather than the widget.
Unmounting
Haavisa.unmount('#haavisa-widget');Mounting twice into the same selector replaces the first instance, so a single-page app re-rendering a route does not stack widgets.