Authentication
One header, two key classes. Getting the class wrong is the most expensive mistake you can make here, so the difference is worth two minutes.
Authorization: Bearer hv_live_xxxxxxxx_secret
x-api-key: hv_live_xxxxxxxx_secretTwo classes of key
| Secret | Publishable | |
|---|---|---|
| Prefix | hv_live_ / hv_test_ | hv_pub_live_ / hv_pub_test_ |
| Lives on | Your server | Your public web page |
| Scopes | Whatever you grant | check:read, products:read only |
| Origin-bound | No | Yes — only listed origins |
| Can write | Yes, if scoped | Never |
Publishable keys
Use one for the widget, or for your own browser code that calls /v1/check and /v1/products. It is capped server-side to those two read scopes no matter what you request, and it only works from origins you list.
curl -X POST https://api.haavisa.com/v2/keys \
-H "Authorization: Bearer $HAAVISA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Booking site widget",
"kind": "publishable",
"allowed_origins": ["https://bookings.example.com"]
}'{
"key": "hv_pub_live_xxxxxxxx_secret",
"scopes": ["check:read"],
"dropped_scopes": ["applications:write"],
"dropped_reason": "Publishable keys are limited to check:read and products:read."
}dropped_scopes names anything you asked for and did not get, so you are never building against a capability you were refused.
A publishable key with no origins permits nothing. That fails closed on purpose: forgetting to configure the allowlist must not produce a key that works everywhere.
Scopes
check:read— eligibility checksproducts:read— the catalogueapplications:read/applications:write— the application lifecyclereports:read— usage and volumeskeys:write— issue and revoke your own keysprojects:write— internal only; no partner key is ever issued it
Test keys
hv_test_ and hv_pub_test_ work against the same host. Calls made with one are recorded for your usage graph but are never billed.
There is no separate sandbox host. That is a real difference from what you may expect, and it is stated here so you do not spend an afternoon looking for one.
The partner portal signs in differently
A human does not use an API key. The portal at <portal-url> authenticates with a single-use email link and an HttpOnly session cookie. Requiring a key to obtain a key would be a circle.