Back to docs
API Reference

API Authentication

How Koji Headless API authentication works: pk_live_ API keys, permissions, session tokens, and origin allowlists.

API Authentication

Every request to the Koji Headless API authenticates with an API key scoped to a single study. The key both identifies the study and controls what the caller may do, which is why you never send a project id to the interview endpoints. This guide covers the key format, where keys come from, the permission model, session tokens, origin allowlists, and how to keep all of it safe.


How authentication works

Send your key in the Authorization header with the Bearer scheme:

Authorization: Bearer pk_live_wJalrXUtnFEMI4K7MDENGbPxRfiCYz2K

A complete authenticated request looks like this:

curl -X POST https://www.koji.so/api/v1/interviews/start \
  -H "Authorization: Bearer pk_live_wJalrXUtnFEMI4K7MDENGbPxRfiCYz2K" \
  -H "Content-Type: application/json" \
  -d '{
    "respondent": { "external_id": "user_8271", "display_name": "Jamie" },
    "mode": "text"
  }'

Two credentials are involved in a full interview session:

  • The API key (pk_live_...) is your developer credential. It identifies the study and carries the permissions that gate each endpoint.
  • The session token is a per-interview respondent credential. The start endpoint issues it, and the message and complete endpoints require it in the X-Session-Token header.

Here is what each endpoint expects:

EndpointAPI keySession tokenPermission
POST /interviews/startYesNointerview:start
POST /interviews/{interview_id}/messageYesYesinterview:chat
POST /interviews/{interview_id}/completeYesYesinterview:complete
GET /interviews/{interview_id}YesNointerview:read
GET /embed/{project_id}OptionalNoNone

Key format

pk_live_ followed by 32 URL-safe base64 characters. Keys are stored hashed. The full key is shown exactly once, at creation.

Two practical consequences:

  • Copy the key when the create dialog shows it. There is no way to view it again later.
  • Because only a hash is stored, keys cannot be read out of Koji's database. If a key leaks from your side, revoke it and create a new one.

Creating a key

Open your study, click the three-dot menu → Integrate → Advanced tab → API Keys → Create API Key. Select the permissions the key needs at creation; new keys default to all four.

For rotating, revoking, and auditing keys, see Managing API Keys.


Permissions

Each key carries a permission set that is checked on every request:

PermissionWhat it doesGrants
interview:startStart interviewsPOST /interviews/start
interview:chatSend messagesPOST /interviews/{id}/message
interview:completeComplete interviewsPOST /interviews/{id}/complete
interview:readRead interview dataGET /interviews/{id}

A key missing the required permission gets a 403 with a body like:

{
  "error": "API key does not have interview:start permission"
}

Least-privilege setups and the full permission model are covered in API Key Permissions.


Session tokens

  • Issued by the start endpoint as session_token. A bare UUID (no prefix).
  • Scoped to a single interview session; required on the message and complete endpoints.
  • Acts as the respondent-side credential: treat it as a secret for that interview.
  • The read endpoint (GET /interviews/{id}) does not need it. It authenticates with the API key alone.

The start endpoint returns the token as session_token in its 201 response. See Starting Interviews via API for the full response shape.


Origin allowlists

  • Each key has an optional list of allowed origins, checked against the browser's Origin header.
  • An empty list allows all origins.
  • Requests without an Origin header (curl, server-to-server) always pass the origin check. Origin allowlists protect against browser-based misuse, not server callers.
  • Exact entries match the full origin including scheme, e.g. https://app.example.com.
  • Wildcard entries like *.example.com match the hostname of any subdomain plus the apex domain (any port). Lookalike domains such as evil-example.com do not match.
  • A * entry allows all origins explicitly.

Security best practices

  • Keep keys on your server whenever you can. Server-to-server calls are the safest way to use the API.
  • If a key must ship to the browser (for example as the embed widget's api_key parameter), create a dedicated key with only the permissions that flow needs, and set an origin allowlist so other sites cannot reuse it.
  • Give read access (interview:read) only to server-side keys. Transcripts and analysis do not belong in the browser.
  • Treat each session_token as a secret for its interview. Do not log it, and never share it between respondents.
  • Revoke keys you no longer use. A revoked key fails with Invalid API key on the next request.

Plans and credits

The headless API is available on all plans. Usage is governed by interview credits: each interview consumes credits from the study owner's balance (text and voice at different rates), and the start endpoint rejects new interviews when the study is out of quota or the owner has no credits.


FAQ

Can one key work across multiple studies?

No. Keys are scoped to a single study. The key determines which study an interview belongs to, which is why the start endpoint takes no project id. Create one key per study.

I lost my key. Can I recover it?

No. Keys are stored hashed and shown in full exactly once, at creation. Create a new key and revoke the old one.

Do server-to-server calls need an origin allowlist?

No. Requests without an Origin header, such as curl or backend calls, always pass the origin check. Origin allowlists protect against browser-based misuse, not server callers.

Is the session token a second API key?

No. The API key is your developer credential and works across all endpoints for its study. The session token is issued per interview and only proves that a message or complete request belongs to that respondent's session. The read endpoint does not use it at all.