Quickstart
Install an SDK, initialize it with an API key, and protect a route — verifying user sessions against the public JWKS.
This guide gets you from zero to a protected route. You'll create an API key, initialize a server SDK, and verify an incoming user access token locally against Qeet ID's published ES256 JWKS — no network round-trip per request.
Two integration styles: a server SDK (TypeScript/Go/Python) that verifies tokens
and manages users, and the @qeetid/nextjs + @qeetid/react pair that runs the
full hosted-login OAuth flow for you. This page covers the server SDK; for the
drop-in web flow jump to SDKs → Next.js.
1. Create an API key
API keys authenticate your backend to the Qeet ID API. Create one in the admin dashboard, or via the API with an admin token:
/v1/api-keysCreate an API keycurl -X POST https://api.qeetid.com/v1/api-keys \
-H "Authorization: Bearer $QEETID_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"backend"}'The response includes the secret once — a qk_… value. Store it as a server
secret; never ship it to a browser.
API keys are server-only
A qk_… key has tenant-scoped access to the API. Keep it in an environment
variable or secret manager. For the browser, use the hosted-login flow
(@qeetid/nextjs) — the secret stays on the server.
2. Install a server SDK
pnpm add @qeetid/sdkgo get github.com/qeetgroup/qeetid-gopip install qeetid3. Initialize the client
The API key goes in via the SDK config — the SDK sends it as
Authorization: ApiKey qk_… on every request.
import { Qeetid } from "@qeetid/sdk";
export const qeetid = new Qeetid({
apiKey: process.env.QEETID_API_KEY!, // qk_…
// baseUrl defaults to https://api.qeetid.com
});import (
"os"
qeetidsdk "github.com/qeetgroup/qeetid-go"
)
var qeetid = qeetidsdk.New(qeetidsdk.Options{
APIKey: os.Getenv("QEETID_API_KEY"), // qk_…
})import os
from qeetid import Qeetid
qeetid = Qeetid(api_key=os.environ["QEETID_API_KEY"]) # qk_…4. Verify a session
Your app receives a user access token (an ES256 JWT) — typically as a bearer header from your frontend. Verify it locally: the SDK checks the signature against the cached JWKS, then expiry, issuer, and audience. No per-request network call.
const claims = await qeetid.sessions.verify(accessToken);
// claims.userId, claims.tenantId, claims.sessionIdclaims, err := qeetid.Sessions.Verify(ctx, accessToken)
// claims.UserID, claims.TenantIDclaims = qeetid.sessions.verify(access_token)
# claims.user_id, claims.tenant_idLocal verification scales
Because tokens are ES256 (asymmetric) and the JWKS is public, your services verify
tokens offline after a one-time key fetch. Rotating signing keys publishes a new
kid; the SDKs refetch automatically. See Sessions.
5. Protect a route
Combine verification with an authorization check. qeetid.can(...) is a single call
to GET /v1/check.
import { qeetid } from "@/lib/qeetid";
export async function POST(req: Request) {
const token = req.headers.get("authorization")?.replace("Bearer ", "");
if (!token) return new Response("Unauthorized", { status: 401 });
const claims = await qeetid.sessions.verify(token);
const ok = await qeetid.can({
user: claims.userId,
tenant: claims.tenantId!,
permission: "billing:write",
});
if (!ok) return new Response("Forbidden", { status: 403 });
// … do the work
return Response.json({ ok: true });
}claims, err := qeetid.Sessions.Verify(ctx, token)
if err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
ok, err := qeetid.Can(ctx, qeetidsdk.PermissionCheck{
User: claims.UserID,
Tenant: claims.TenantID,
Permission: "billing:write",
})
if err != nil || !ok {
http.Error(w, "forbidden", http.StatusForbidden)
return
}claims = qeetid.sessions.verify(token)
if not qeetid.can(user=claims.user_id, tenant=claims.tenant_id, permission="billing:write"):
return Response(status=403)Prefer a full web flow?
If you're building a Next.js app, @qeetid/nextjs runs the entire hosted-login
OAuth flow (Authorization Code + PKCE), stores an encrypted HttpOnly session cookie,
and silently refreshes near-expiry sessions in middleware — no token handling in
your code. Pair it with @qeetid/react for <SignedIn> / <SignedOut> and
useUser().
import { qeetidMiddleware } from "@qeetid/nextjs/middleware";
export default qeetidMiddleware({ publicRoutes: ["/", "/pricing"] });
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};See SDKs → TypeScript & Next.js for the complete setup.
Next steps
Qeet ID
An open-source, self-hostable, passkeys-first identity platform — OIDC + SAML provider, SCIM, RBAC, and a tamper-evident audit log, with no SSO tax.
Core concepts
The primitives Qeet ID is built on — tenants & organizations, users, principals, sessions, and roles — and how identity flows between them.