Appearance
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 userNever 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 key | Session JWT | |
|---|---|---|
| Format | sk_… in header x-api-key | Authorization: Bearer <token> or WebSocket auth.token |
| Who holds it | Your backend only | Browser / mobile app |
| Lifetime | Long-lived (rotate on your schedule) | Short-lived (default 1 hour, max 24 hours) |
| Identifies | Your account (whole organization; tenant in the API) | One user in your account |
| Typical use | Upsert users, create rooms, mint tokens | Connect, 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(afterconnect())
Cannot do:
- Mint tokens —
POST /auth/tokenrequires the API key - Change tenant settings —
PATCH /tenant/settingsrequires the API key - Upsert users —
POST /usersrequires 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-tokensonly 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
AuthorizationandContent-Typeonly — notx-api-key(seeapps/gateway/src/main.ts). - Server-to-server calls with
x-api-keyare 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
| Task | Where it runs | Credential |
|---|---|---|
| Upsert user profile | Your server | API key |
| Mint session token | Your server | API key |
| Create rooms, add participants | Your server | API key |
| Push / tenant app setup | Your server | API key |
| Connect and chat | Browser / app | Session JWT |
Simple rule: backend orchestrates; the client only chats as the logged-in user.
WebSocket auth
| Method | Use 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
| Problem | Cause | Fix |
|---|---|---|
404 on POST /auth/token | User not registered in Sendsar | Call POST /users first |
403 Cannot act as another user | Client passed a different userId | Use session JWT; omit userId where the SDK infers it |
| API key exposed in browser | Key in front-end env or bundle | Mint tokens on your server only |
| REST works, WebSocket stale after refresh | Bearer updated but socket not reconnected | Call connect() again (or use createSessionManager / SendsarProvider) |
| No online presence | WebSocket connected with API key | Connect with auth.token (session JWT) |
401 on admin routes from client | Client used JWT where API key is required | Perform admin calls on your server with x-api-key |
Next steps
- Quick start — minimal end-to-end path
- Server setup — session route and environment
- Client SDKs — connect and build chat UI
- Push notifications — optional offline push
- Webhooks — optional server events
- API Reference — full endpoint list (Scalar)