Authentication
On this page
dembrane accepts three credentials. None of them is an API key: there are no API keys, personal access tokens or static tokens.
| You are building | Credential | Where it comes from |
|---|---|---|
| Something an assistant or agent uses | An OAuth access token | The person approves your client on a consent page. See MCP. |
| A recorder for participants | The participant token | The response to the call that starts a conversation. See the participant API. |
| A receiver for events | None. dembrane calls you. | You check the signature. See webhooks. |
| A call as a signed-in person | A session | The person signs in to the dashboard. |
Sessions#
Sign-in and sessions are Better Auth, served by the API under /api/auth/*.
| Topic | How it works |
|---|---|
| Ways to sign in | Email and password, a one-time code by email (6 digits, valid 10 minutes), Google, and a second factor with TOTP. |
| Passwords | 8 to 256 characters. A new account must verify its email before it can sign in with a password. |
| Lifetime | A session lasts 7 days and is extended once it is older than 1 day. |
| Cookie | <prefix>.session_token. The default prefix is dembrane. Outside local development the cookie is secure and its name starts with __Secure-. |
| Bearer | The API reads the session from the cookie or from an Authorization: Bearer header. Either way it is the same session of a person who signed in. |
| One browser at a time | A dashboard sign-in while the account is signed in on another browser signs nobody in until the person chooses to replace the other sessions. |
| Ending | A password reset ends the person’s sessions. A suspended or archived account gets no session. |
A route that needs a person answers 401 with the code auth.session_expired when there is no valid session.
Important
dembrane has no supported way to issue a long-lived credential for a script. Do not build an unattended integration on a person's session. Use webhooks to receive data and MCP to read it.
Who may do what#
A session says who someone is. What they may do comes from their role in the organisation and the workspace, and from the workspace’s plan. See roles & permissions.
Routes under /api/v2/admin/* are for dembrane staff and are internal. No route can make someone staff.
OAuth tokens for agents#
An agent that connects through MCP gets a token pair bound to one person’s grant.
| Token | Prefix | Lifetime |
|---|---|---|
| Access token | dbr_at_ |
1 hour |
| Refresh token | dbr_rt_ |
30 days |
The scopes are read and write. The agent sends the access token as Authorization: Bearer. It sees what the person sees in the organisations they chose, and it is never staff. The full flow is on the MCP page.
The participant token#
A participant has no account and no session. The call that starts a conversation returns a token in the response header x-participant-token. Its format is p1.<body>.<signature>.
Store it and send it back in the x-participant-token request header on every call that touches that conversation. A call with a wrong token is refused with 403 and the code participant.token_invalid.
Errors#
Every error from the API is one JSON body with four fields: detail, code, params and action. Match on code (for example auth.session_expired), not on the text in detail.
If you run dembrane yourself#
| Setting | What it does |
|---|---|
AUTH_SECRET |
Signs sessions and tokens. At least 32 characters. Changing it signs everyone out. |
AUTH_COOKIE_DOMAIN |
A parent domain shared by the dashboard, the portal and the API, so one sign-in covers all three. |
AUTH_COOKIE_PREFIX |
The first part of the cookie names. Changing it signs everyone out. |
AUTH_GOOGLE_CLIENT_ID, AUTH_GOOGLE_CLIENT_SECRET |
Google sign-in. Unset hides the option. |
The rest is on the configuration page.
Comments