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.
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
| Parameter | Required | Description |
|---|---|---|
client_id | Yes | Your API client's id (see API Clients) |
response_type | Yes | Always code |
redirect_uri | Yes | Must exactly match one of the client's configured Redirect URLs |
state | No | Round-tripped back to you unchanged — use a random value to protect against CSRF |
scope | No | Accepted 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
Referer header on this request must match one of them. Most browsers send it by default, but strict privacy settings or referrer-stripping proxies can break this silently — if users get an unexpected error here, check that first.
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.
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.
From your backend (never the browser), call:
GET/api/v1/authorise
api/v1 endpoint (see API Overview), this one takes client_id and api_secret as query parameters, not X-Client-Id/X-Api-Secret headers. This is the one exception, kept for compatibility with how this endpoint has always worked.
| Parameter | Required | Description |
|---|---|---|
code | Yes | The code from Step 2 |
client_id | Yes | Same client id used in Step 1 |
api_secret | Yes | Your 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.
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.
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.
Validation and not-found errors share one shape:
{
"error": {
"code": "not_found",
"message": "User not found.",
"details": null
}
}
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.
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 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" }'
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"
}'
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"
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"
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"