Developer Documentation

Login Flow

This is how your app lets KPass handle login for its users. Three steps: send the user to KPass, KPass sends them back with a one-time code, you exchange that code for who they are. Your app never sees or stores a password.

Step 1 Send the user to KPass to log in

Redirect the user's browser to KPass's login page. If they already have an active KPass session, they're sent straight back to you with a code — no form shown.

GET/auth/login

ParameterRequiredDescription
client_idYesYour API client's id (see API Clients)
response_typeYesAlways code
redirect_uriYesMust exactly match one of the client's configured Redirect URLs
stateNoRound-tripped back to you unchanged — use a random value to protect against CSRF
scopeNoAccepted but not currently used
https://kpass.klivolks.com/auth/login
  ?client_id=YOUR_CLIENT_ID
  &response_type=code
  &redirect_uri=https%3A%2F%2Fyourapp.example.com%2Fcallback
  &state=a-random-csrf-string

KPass shows its login form, handles the user's credentials, and — if the app requires it — an MFA step. Your app is never involved in any of that.

Step 2 KPass redirects back to your app

Once the user is authenticated, KPass redirects their browser to your redirect_uri:

https://yourapp.example.com/callback?code=AUTHORIZATION_CODE&state=a-random-csrf-string

Check that state matches what you sent in Step 1, then move straight to Step 3 — the code is short-lived and single-use.

Step 3 Exchange the code for the user's identity

From your backend (never the browser), call:

GET/api/v1/authorise

ParameterRequiredDescription
codeYesThe code from Step 2
client_idYesSame client id used in Step 1
api_secretYesYour API client's secret
curl "https://kpass.klivolks.com/api/v1/authorise?code=AUTHORIZATION_CODE&client_id=YOUR_CLIENT_ID&api_secret=YOUR_API_SECRET"

Response:

{
  "userId": "your-own-reference-id-for-this-user",
  "userType": "customer",
  "userName": "jane.doe",
  "userRole": "support-agent",
  "orgId": "..."
}

userId here is the user's Reference Id — your own id for them (see Users) — never KPass's internal id. Missing or wrong api_secret returns 401; an unknown or already-used code returns 400.

That's it — create your own session/cookie/JWT for this user in your app using the identity KPass just gave you. If you also need to check what they're allowed to do once they're in, that's a separate concern — see Permissions.

API Overview

KPass exposes a versioned JSON API at /api/v1 for every resource you can manage — Users, User Groups, Permissions, Apps, and API Clients each have their own section below with copyable examples. This page covers what's common across all of them. If you haven't logged a user in yet, start with Login Flow instead — everything here assumes you already have an API client.

Authentication

Every api/v1 request is authenticated with two headers from one of your app's API Clients — never with a browser session or cookie:

X-Client-Id: YOUR_CLIENT_ID
X-Api-Secret: YOUR_API_SECRET

Missing or invalid credentials return 401 Unauthorized. Keep the secret private — it's shown once, at creation/regeneration time, and never again. The one exception is /api/v1/authorise (part of Login Flow), which takes these as query parameters instead — everything else uses the headers above.

Errors

Validation and not-found errors share one shape:

{
  "error": {
    "code": "not_found",
    "message": "User not found.",
    "details": null
  }
}
Everything is scoped to your organisation

Your API Client's organisation is resolved from the credentials you send — you never pass an orgId yourself, and you can only see or change records that belong to your own organisation.

Where to go next

See Login Flow first if you haven't logged a user in yet. Then Users, User Groups, Permissions, Apps, and API Clients below have the full endpoint list and copyable examples for each — read Permissions in particular before wiring up anything that gates access to a real feature.

Users

Users are the people KPass authenticates for your apps. Each user has a Reference Id — your own identifier for that person in your system — which is what every api/v1 endpoint uses to address a user, never KPass's own internal id.

Create a user — referenceId is your own id for this person; userType/userRole are group ids from the User Groups catalog:

curl -X POST https://kpass.klivolks.com/api/v1/users \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "referenceId": "your-own-user-id",
    "userName": "jane.doe",
    "email": "[email protected]",
    "password": "a-temporary-password",
    "userType": "USER_TYPE_GROUP_ID",
    "userRole": "USER_ROLE_GROUP_ID"
  }'

Get a user by your referenceId:

curl https://kpass.klivolks.com/api/v1/users/YOUR_REFERENCE_ID \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET"

Suspend or reactivate a user:

curl -X PUT https://kpass.klivolks.com/api/v1/users/YOUR_REFERENCE_ID/status \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "status": "suspended" }'

User Groups

Everything in KPass — user roles, user types, departments, arbitrary groups, and permissions themselves — is stored as one kind of group, distinguished by its Group Type. Groups can contain users or other groups, so a role can inherit from another role or department.

Create a group — groupType can be user-role, user-type, or any custom type (never permission; use the Permissions endpoints for those):

curl -X POST https://kpass.klivolks.com/api/v1/groups \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "groupName": "support-agent",
    "groupType": "user-role"
  }'

List groups, optionally filtered by type:

curl "https://kpass.klivolks.com/api/v1/groups?groupType=user-role" \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET"

Add a member (a user or another group) to a group:

curl -X POST https://kpass.klivolks.com/api/v1/groups/GROUP_ID/members \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "memberId": "USER_OR_GROUP_ID",
    "memberType": "user"
  }'

Permissions

Permission strings are dot-namespaced (e.g. kpass.apps.members.create) and form a tree. A user's access to a permission is resolved from, in order of precedence: an explicit override on the user, then an override on one of their groups, then plain membership in a role/group that holds the permission — and at any level, an explicit deny always beats an allow.

Check whether a user can do something — this is the one call your app should make before letting a user perform a sensitive action:

curl -X POST https://kpass.klivolks.com/api/v1/permission/check \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "YOUR_REFERENCE_ID",
    "resource": "myapp.invoices.create"
  }'

Response — always 200, check status/access, not the HTTP status code:

{
  "status": "success",
  "access": "ALLOW",
  "user_role": "support-agent",
  "user_type": "staff"
}

Read a user's full resolved permission tree:

curl https://kpass.klivolks.com/api/v1/users/YOUR_REFERENCE_ID/permissions \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET"

Set a user's permission overrides — full replace, not a diff; omitting a previously-allowed permission revokes it:

curl -X PUT https://kpass.klivolks.com/api/v1/users/YOUR_REFERENCE_ID/permissions \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "permissions": ["myapp.invoices.create", "myapp.invoices.read"]
  }'

List the permission catalog for your organisation:

curl https://kpass.klivolks.com/api/v1/permissions \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET"

Apps

An App represents one of your products or integrations registered with KPass — every user, client, and permission in KPass is scoped to an app's owning organisation. Creating an app also creates its default settings and a default membership group automatically.

Every api/v1 call requires an X-Client-Id / X-Api-Secret header pair from one of the app's API clients — see the Clients section below to create one. Replace YOUR_CLIENT_ID/YOUR_API_SECRET with real values.

Create an app — apps created via the API are linked back to your own product record via referenceId (a 24-character hex id) and are automatically marked as managed by your organisation:

curl -X POST https://kpass.klivolks.com/api/v1/apps \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Product",
    "appURL": "https://myproduct.example.com",
    "referenceId": "YOUR_PRODUCT_REFERENCE_ID"
  }'

List apps in your organisation:

curl https://kpass.klivolks.com/api/v1/apps \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET"

Get a single app by id:

curl https://kpass.klivolks.com/api/v1/apps/APP_ID \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET"

API Clients

An API Client is the credential pair (Client Id + Api Secret) an app uses to call KPass's api/v1 endpoints. Each client belongs to one app. The secret is only ever shown once, right after it's created or regenerated — copy it immediately, KPass never displays it again.

Create a client under an app (the response includes the plaintext secret exactly once):

curl -X POST https://kpass.klivolks.com/api/v1/apps/APP_ID/clients \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "My Service"
  }'

List clients for an app (never includes the secret, only whether one is set):

curl https://kpass.klivolks.com/api/v1/apps/APP_ID/clients \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET"

Regenerate a client's secret — invalidates the old one immediately, any integration using it breaks until updated:

curl -X POST https://kpass.klivolks.com/api/v1/apps/APP_ID/clients/CLIENT_ID/secret \
  -H "X-Client-Id: YOUR_CLIENT_ID" \
  -H "X-Api-Secret: YOUR_API_SECRET"