Skip to content

Service tokens

Tefily service tokens (kfxs_…) are machine credentials owned by a project. They call the data API (/api/p/:projectId/*) and POST /auth/verify. They cannot call console APIs (/api/organizations, schema admin, hosting, …).

They bypass end-user access rules, like a platform token on the data API.

Create them in the console (Developers / service tokens), with the admin SDK projects.createServiceToken, or via MCP (create_service_token). The secret is returned once. The mint call is on the admin page.

The constructor writes the token onto the HTTP client’s bearer before any call. It is not a separate header argument. setToken (JavaScript and Dart) and set_token (Python) do the same thing later. auth.verify then POSTs /api/p/<projectId>/auth/verify with that bearer and body { "token": "<project user jwt>" }.

login on this client replaces kfxs_… with a project JWT. Keep a separate client for the service token, and do not call login on it. A kfx_… personal access token is a different credential: it is not a service token, and it is not what auth.verify sends as the bearer in the samples below.

There is no admin client in JavaScript or Python. These samples are the data-API clients.

  • Password reset, webhooks, and other server routes that read or write collections
  • Verifying a project JWT your frontend sent you
  • Scripts that must not use a human PAT

Do not put kfxs_… in Expo public env, Vite VITE_* that ships to the browser, or a mobile binary.

KataflaxClient accepts serviceToken. auth.verify(token) returns { valid: boolean, user?: Record<string, unknown> }. Await it.

const backend = new KataflaxClient({
baseUrl: 'https://api.tefily.com',
projectId: 'YOUR_PROJECT_ID',
serviceToken: process.env.TEFILY_SERVICE_TOKEN!,
});
const checked = await backend.auth.verify(incomingUserJwt);
if (!checked.valid) {
throw new Error('Invalid session');
}
await backend.collection('password_resets').create({
email,
tokenHash: hash,
});

auth.verify(token) returns ({bool valid, Map<String, dynamic>? user}). Platform is from dart:io.

final backend = KataflaxClient(
baseUrl: 'https://api.tefily.com',
projectId: 'YOUR_PROJECT_ID',
serviceToken: Platform.environment['TEFILY_SERVICE_TOKEN']!,
);
final checked = await backend.auth.verify(incomingUserJwt);
if (!checked.valid) {
throw Unauthorized();
}
await backend.collection('password_resets').create({
'email': email,
'tokenHash': hash,
});

auth.verify(token) returns VerifyResult (valid, user), exported from kataflax. The call is synchronous.

import os
from kataflax import KataflaxClient
backend = KataflaxClient(
base_url="https://api.tefily.com",
project_id="YOUR_PROJECT_ID",
service_token=os.environ["TEFILY_SERVICE_TOKEN"],
)
checked = backend.auth.verify(incoming_user_jwt)
if not checked.valid:
raise PermissionError("Invalid session")
backend.collection("password_resets").create({
"email": email,
"tokenHash": hash,
})
POST /api/p/{projectId}/auth/verify
Authorization: Bearer kfxs_…
Content-Type: application/json
{"token":"<project user jwt>"}

Response: { "success": true, "data": { "valid": true, "user": { ... } } } (shape may wrap valid / user in data).