Authentication
Three identity levels: anonymous publishable key, signed publishable key, your own token.
Every request to the public endpoints (/v2/token, /v2/agents,
/v2/conversations, /v2/chat, /v2/runs) authenticates with exactly one
of:
- a chat session token (
Authorization: Bearer <token>) — issued to a specific end-user, short-lived; - an API key (
X-API-Key: <key>) — used when your backend calls directly on behalf of an end-user, see Server-to-server.
This page is about getting a token. There are three identity levels, each with its own way of obtaining one — pick based on whether you want the end-user to have an identity at all, and if so, who's vouching for it.
Three identity levels
1. Anonymous (unsigned pk)
The simplest. The widget generates its own random anonymous_id (a UUID),
stores it in localStorage, and sends it along with pk to get a token.
You don't need to know who this end-user is. Good for a public FAQ
assistant that doesn't need to tie back to a real user account.
<harnix-chat pk="pk_xxxxxxxxxxxxxxxx"></harnix-chat>No other parameters needed — the widget handles the rest.
2. Identified, signed by you (pk + user-hash)
You know who this end-user is (they're signed into your product, say), but you don't want to expose an API key in the browser. Your backend signs a short-lived HMAC signature and passes it to the widget through three attributes:
<harnix-chat
pk="pk_xxxxxxxxxxxxxxxx"
end-user-id="user_42"
user-hash="a94a8fe5ccb19ba61c4c0873d391e987982fbbd3..."
expires-at="1735689600"
></harnix-chat>The widget sends all three fields to POST /v2/token; Harnix verifies the
signature with your organization's identity secret (which never leaves
your backend). See the signature recipe and code examples on
Token exchange.
No silent fallback to anonymous
If you send end-user-id without user-hash or expires-at, the
request is rejected (invalid_user_hash) — it does not silently fall
back to anonymous. Minting an identified token without a valid signature
is something Harnix deliberately refuses.
3. Your own token (token-url or token)
You want full control over token issuance — maybe you have extra logic to run before allowing chat, or you already have an internal token-issuing endpoint. Two options:
The widget POSTs to your endpoint (same-origin, with the current session
cookies) whenever it needs a fresh token:
<harnix-chat token-url="/api/harnix-token"></harnix-chat>That endpoint is yours, not Harnix's — it authenticates the visitor
however your product does (a session cookie, typically), then calls
POST /v2/token server-side with an API key, and returns
{ "token": "..." }.
Your token endpoint must authenticate first
Mint a token only for the visitor your own session says is signed in, and only for that visitor's id. An endpoint that hands out tokens without checking the session lets anyone chat as — and read the conversations of — any of your users.
Exactly one of three
pk, token-url, token — the widget requires exactly one of these
attributes. Having two (or none) is a configuration error; the widget
refuses to render rather than guess.
Origin
Both pk (direct token exchange) and an issued token (every subsequent
request) are checked against the Allowed origins configured for your
organization (Console → Integration). It's an exact scheme + host + port
match, no wildcard subdomains. A mismatched origin gets you
origin_not_allowed (403).
Next
- Token exchange — the full
POST /v2/tokencontract, including how to signuser-hash. - Server-to-server — calling with an API key, no widget involved.
- Errors — what each authentication-related error code means.