Skip to content

Authentication

New here?

Follow the Quick start first — this page explains the auth model in depth.

Sendsar uses two credentials. Pick the right one and integration stays simple.

Your server  →  API key (sk_…)     →  admin + mint tokens
Your app     →  session JWT        →  chat as one user

Never put the API key in a browser or mobile app. Never use a session JWT to mint other tokens or change account settings (PATCH /tenant/settings).

Terminology

Account = your Sendsar organization (identified by your API key). The API and codebase call this a tenant (tenantId, /tenant/settings, …). End-users live inside your account as chat users.

See Server setup for implementation examples.


At a glance

API keySession JWT
Formatsk_… in header x-api-keyAuthorization: Bearer <token> or WebSocket auth.token
Who holds itYour backend onlyBrowser / mobile app
LifetimeLong-lived (rotate on your schedule)Short-lived (default 1 hour, max 24 hours)
IdentifiesYour account (whole organization; tenant in the API)One user in your account
Typical useUpsert users, create rooms, mint tokensConnect, send/receive messages, typing, presence

What each credential can do

API key — server / admin

Use on your backend with the x-api-key header.

Can do:

  • Upsert users — POST /users
  • Mint session tokens — POST /auth/token
  • Create and manage rooms, participants, messages (as any user, when you pass userId / senderId)
  • Manage tenant apps and push configuration — /tenant-apps/*
  • Update tenant settings — PATCH /tenant/settings
  • Register device tokens for any user — POST /users/:id/device-tokens (API key)

Cannot be used by: browser or mobile clients (keep it off the client entirely).

Session JWT — end-user client

Minted by your server via POST /auth/token. Safe to pass to logged-in users.

Can do:

  • Connect WebSocket with auth.token
  • Chat REST: list my rooms, read/send messages, reactions, read receipts
  • Typing and presence (when connected with auth.token)
  • Read tenant chat settings — GET /tenant/settings
  • Register own push token — POST /users/me/device-tokens (after connect())

Cannot do:

  • Mint tokens — POST /auth/token requires the API key
  • Change tenant settings — PATCH /tenant/settings requires the API key
  • Upsert users — POST /users requires the API key
  • Act as another user — requests are scoped to the user embedded in the JWT
  • Register push for another user — use /users/me/device-tokens only for yourself

One user per token

If a client sends userId or senderId that does not match the JWT, the API returns 403 Cannot act as another user.

Browser CORS

Tenant apps on any domain call the gateway with a session JWT. The gateway allows browser origins broadly; tenant isolation is auth, not CORS.

  • Browsers may send Authorization and Content-Type only — not x-api-key (see apps/gateway/src/main.ts).
  • Server-to-server calls with x-api-key are unaffected (CORS does not apply to backends).

Why upsert the user before minting a token?

Your session route should call POST /users before POST /auth/token.

Sendsar does not know your users until you register them. Token minting checks that the user exists — otherwise you get 404 User not found.

Upsert is idempotent: call it on every session request. It creates the user the first time and updates username / avatar on later calls.


Who calls what

TaskWhere it runsCredential
Upsert user profileYour serverAPI key
Mint session tokenYour serverAPI key
Create rooms, add participantsYour serverAPI key
Push / tenant app setupYour serverAPI key
Connect and chatBrowser / appSession JWT

Simple rule: backend orchestrates; the client only chats as the logged-in user.


WebSocket auth

MethodUse for
auth: { token: "<jwt>" }Production — browsers and mobile apps
auth: { apiKey: "sk_…" }Server/debug only — not for end-user apps

Session JWT connections get tenant presence (online/offline). API-key WebSocket connections do not.


Common mistakes

ProblemCauseFix
404 on POST /auth/tokenUser not registered in SendsarCall POST /users first
403 Cannot act as another userClient passed a different userIdUse session JWT; omit userId where the SDK infers it
API key exposed in browserKey in front-end env or bundleMint tokens on your server only
REST works, WebSocket stale after refreshBearer updated but socket not reconnectedCall connect() again (or use createSessionManager / SendsarProvider)
No online presenceWebSocket connected with API keyConnect with auth.token (session JWT)
401 on admin routes from clientClient used JWT where API key is requiredPerform admin calls on your server with x-api-key

Next steps

Sendsar — headless chat for B2B platforms