Tokens and authentication
Every Web API call carries a token in the Authorization header:
Authorization: Bearer kbot_…The workspace is implied by the token. A missing, invalid, expired or revoked token answers 401 invalid_token with
a WWW-Authenticate header.
Credential formats
Section titled “Credential formats”| Credential | Format | Where it comes from | Lifetime |
|---|---|---|---|
| Bot token | kbot_ + 43 characters |
API access page, or kit.access with tokenType: bot |
No expiry; rotate or revoke |
| User token | kusr_ + 43 characters |
kit.access with tokenType: user |
1 hour |
| Refresh token | kref_ + 43 characters |
With every user token | Single use |
| Authorization code | kcode_ + 43 characters |
The authorize redirect | 10 minutes, single use |
| Client secret | kcs_ + 43 characters |
API access page (private Kits) | Until replaced; the previous one works 24 hours |
| Webhook URL | https://hooks.ketvia.com/v1/…/…/<secret> |
API access page | Until revoked |
The 43 characters are base64url (256 random bits). Every secret is shown once, when it is created, and stored only as a hash. If you lose one, revoke it and create a new one.
Bot tokens
Section titled “Bot tokens”A bot token acts as the Kit bot of one installation. It sees only channels the Kit bot was added to, and edits or deletes only messages the Kit bot posted.
Create one on the Kit’s API access page (admins), or through the kit.access flow
with token_type=bot, which an admin must approve.
Revoke it on the API access page. Revocation is immediate.
Rotate it from your code without downtime:
curl -s -X POST https://api.ketvia.com/api/v1/auth/rotate \ -H "Authorization: Bearer $KETVIA_TOKEN"{ "token": "kbot_…", "previousTokenExpiresAt": "2026-10-06T09:30:00.000Z"}The new token is shown once. The token you called with keeps working for 24 hours, then expires. A token can be rotated only once: rotate again with the new token, not the old one.
User tokens
Section titled “User tokens”A user token acts as one member and is bounded by that member’s access: it sees what they see and posts as them. User tokens:
- last 1 hour (
expiresIn: 3600); - come with a single-use refresh token: each refresh returns a new access token and a new refresh token;
- if a refresh token is used twice, Ketvia treats it as stolen and revokes the whole grant (all access and refresh tokens from that authorization);
- end when the member’s web session that authorized them ends (for example when they sign out).
Use user tokens for actions a Kit bot cannot take in v1: adding and removing reactions, and uploading files.
The kit.access flow
Section titled “The kit.access flow”kit.access lets your Kit’s own backend obtain tokens: a bot token for the installation (an admin approves) or a
user token for one member (any member approves for themselves). It is an OAuth-style authorization code flow.
Before you start, on the Kit’s API access page:
- note the client ID (the Kit id);
- generate a client secret (
kcs_…, shown once); - check the redirect URLs: they come from the manifest’s
oauth.redirectUrls, must behttps, and theredirect_uriyou send must equal one of them.
-
Send the member to Ketvia. For a user token, first create a PKCE verifier (43 to 128 characters from
A–Z a–z 0–9 . _ ~ -) and its S256 challenge,base64url(sha256(verifier))without padding. Keep the verifier and a randomstatein the member’s session on your side.https://ketvia.com/app/kits/authorize?workspace=<workspaceId>&kit=<slug>&token_type=user&redirect_uri=https%3A%2F%2Fkit.acme.example%2Fketvia%2Fcallback&state=<opaque>&code_challenge=<S256 challenge>(Shown on several lines for reading; it is one URL.)
token_typeisbotoruser.code_challengeis required for user tokens. A bot authorization must be allowed by a workspace admin. -
The member allows it. Ketvia shows who is asking for what and, after the member allows it, redirects to:
https://kit.acme.example/ketvia/callback?code=kcode_…&state=<opaque>Check that
statematches what you stored. The code is valid for 10 minutes and works once. -
Exchange the code from your backend (never from a browser):
Terminal window curl -s https://api.ketvia.com/api/v1/oauth/kit.access \-H 'Content-Type: application/json' \-d '{"grantType": "authorization_code","clientId": "<kit id>","clientSecret": "kcs_…","code": "kcode_…","redirectUri": "https://kit.acme.example/ketvia/callback","codeVerifier": "<the PKCE verifier>"}'redirectUrimust equal the one the code was issued for.codeVerifieris required for user tokens.
A bot authorization returns:
{ "tokenType": "bot", "accessToken": "kbot_…", "botId": "9c4e2b7a-1d3f-4a6e-b8c5-0f2d7e6a9b14", "installationId": "5d1a8e7c-93b2-4f3e-8c0d-2a6b4e9f1c37", "workspace": { "id": "0b9f6c1e-2f4d-4c55-9a51-6f7f3c1d2e10", "name": "Acme" }, "scopes": ["conversations:read", "messages:write"]}A user authorization returns:
{ "tokenType": "user", "accessToken": "kusr_…", "refreshToken": "kref_…", "expiresIn": 3600, "userId": "2f8b6d1c-7e4a-4c9b-a3d5-6e1f0a8c2b97", "installationId": "5d1a8e7c-93b2-4f3e-8c0d-2a6b4e9f1c37", "workspace": { "id": "0b9f6c1e-2f4d-4c55-9a51-6f7f3c1d2e10", "name": "Acme" }, "scopes": ["messages:read", "reactions:write"]}Refreshing a user token
Section titled “Refreshing a user token”Before the hour is up, exchange the refresh token:
{ "grantType": "refresh_token", "clientId": "<kit id>", "clientSecret": "kcs_…", "refreshToken": "kref_…"}The answer has the same shape as a user authorization, with a new refresh token. Store it and discard the old one: the old refresh token is spent, and using it again revokes the whole grant.
With the SDK, exchangeKitAccess(request, { baseUrl }) sends either request and returns the parsed answer. It does
not retry after network errors, because codes and refresh tokens are single-use.
Client secrets
Section titled “Client secrets”The client secret authenticates your backend in POST /oauth/kit.access. Generate it on the Kit’s API access page.
Generating a new secret shows it once; the previous secret keeps working for 24 hours so you can deploy the new one
without downtime.
Which scopes a token has
Section titled “Which scopes a token has”A token carries the scopes that were granted when it was issued, limited to what the installed manifest version
still requests. GET /auth/test returns these effective scopes. If a new manifest version drops a scope, existing
tokens lose it at once.
Checklist
Section titled “Checklist”- Keep tokens, refresh tokens, client secrets and webhook URLs in a secret store, never in code or logs.
- Never send a token to a browser. The
kit.accessexchange happens server-side. - Rotate bot tokens with
POST /auth/rotateand deploy the new token within 24 hours. - Revoke anything you suspect has leaked: revocation is immediate.