Flambe docs
Guides

Authentication

How to obtain a bearer token, what it scopes, and how server-to-server access works.

Every endpoint except /health requires a bearer token:

Authorization: Bearer <token>

Tokens are Clerk session JWTs. Flambe does not issue its own API keys to third parties — the token's sub claim is the Flambe user id, and it is what scopes every request. A token can only ever see and modify what that user can see and modify.

From a browser or mobile client

If you already use a Clerk SDK, ask it for the current session token:

import { useAuth } from '@clerk/nextjs';

const { getToken } = useAuth();

async function authedFetch(path: string, init: RequestInit = {}) {
  const token = await getToken();

  return fetch(`https://api.flambe.dev${path}`, {
    ...init,
    headers: {
      ...init.headers,
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
  });
}

getToken() returns a short-lived token and refreshes it as needed. Call it per request rather than caching the string — that is the single most common cause of spurious 401s.

From a server or a script

Use a Clerk backend SDK to mint a token for the user you are acting as. Never ship a Clerk secret key to a client.

import { createClerkClient } from '@clerk/backend';

const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! });

// Exchange a session for a short-lived JWT.
const { jwt } = await clerk.sessions.getToken(sessionId, 'default');

For a long-running integration with no interactive user, create a dedicated Flambe account for it and drive that account's sessions.

Reading the 401

The two failures mean different things and want different handling:

BodyMeaningWhat to do
{"error":"Access token required"}The Authorization header was absent entirely.Fix the client. Do not refresh — there was no session to refresh.
{"error":"Invalid or expired token"}The header arrived but the JWT did not verify.Refresh the session and retry once.

Treating Access token required as an expired session sends clients into a refresh loop that can never succeed, because the header was never being sent in the first place. Branch on the message.

Verifying a token

GET /api/users/me is the cheapest way to check a token and learn who it belongs to:

curl https://api.flambe.dev/api/users/me \
  -H "Authorization: Bearer $FLAMBE_TOKEN"
{
  "id": "user_2abcDEF",
  "email": "you@example.com",
  "household_ids": ["1f0e2d3c-4b5a-6978-8796-a5b4c3d2e1f0"]
}

Endpoints that need no token

Two, deliberately:

  • GET /health — the liveness probe.
  • GET /api/shared/{token} — resolves a public share link so a recipient without an account can view it.

Everything else, including POST /api/shared/{token}/save, is authenticated.

Legacy username/password

POST /api/auth/register and POST /api/auth/login still exist for older clients and are marked deprecated. New integrations should not use them.

On this page