Appbricx
Docs
FeaturesPricingStart free

Get started

OverviewWhat Appbricx isQuickstart with a coding agentQuickstart with the builderCore concepts

Guides

Data and tablesAccess and rolesBusiness rulesSign-inCustom screensRealtime and topicsWorkflows, schedules and webhooksServer functionsAI in your appPaymentsFilesMessagesIntegrationsVerifyPublishing, domains and rollbackOperating your appExport and self-hostSelf-host install

Tutorials

Build a booking app with Claude CodeMulti-user todo listClient portalSlack on new sign-upTemplates

Reference

Reference homeFrontend SDKHTTP APIMCP toolsCLIIntegrations catalogContract formatAI key resolutionRun Appbricx locally

Sign-in API

The app's own end-user accounts: sign-up, password and magic-link sign-in, social sign-in, two-factor codes, sessions, admin user management and organisations.

34 endpointsHTTP API referenceGuide: Let your users sign in

On this page
  1. Authentication
  2. Endpoints
  3. POST /auth/signup
  4. POST /auth/login
  5. GET /auth/me
  6. GET /auth/signup-options
  7. POST /auth/logout
  8. POST /auth/verify/request
  9. GET /auth/verify/confirm
  10. POST /auth/reset/request
  11. POST /auth/reset/confirm
  12. POST /auth/magic/request
  13. GET /auth/magic/consume
  14. GET /auth/users
  15. GET /auth/roles
  16. POST /auth/users
  17. PATCH /auth/users/:id
  18. GET /auth/set-password
  19. POST /auth/set-password
  20. GET /auth/oauth/providers
  21. GET /auth/oauth/:provider/start
  22. GET /auth/oauth/:provider/callback
  23. GET /auth/oauth/complete
  24. POST /auth/oauth/config
  25. DELETE /auth/oauth/config/:provider
  26. POST /auth/mfa/enroll/start
  27. POST /auth/mfa/enroll/confirm
  28. POST /auth/mfa/verify
  29. POST /auth/mfa/disable
  30. POST /auth/mfa/backup-codes/regenerate
  31. POST /orgs/invite
  32. GET /orgs/invite/accept
  33. GET /orgs/members
  34. PATCH /orgs/members/:userId
  35. DELETE /orgs/members/:userId
  36. GET /orgs/current

Base URL: your app's origin ($APP_ORIGIN): the published host, a custom domain, or the platform origin for the builder preview. Errors answer { ok: false, error: { code, message } } unless noted. Add x-appbricx-env: dev or prod to pick the database explicitly; otherwise the host decides (preview means dev, a published host means prod).

Authentication

App key
A project key or preview token identifies the app. No user session is needed. Authorization: Bearer $APP_KEY
App key + admin session
A project key and the session of an app user with the admin flag. Anyone else gets 403. Authorization: Bearer $APP_KEYx-appbricx-app-session: $ADMIN_SESSION
App key + signed-in user
A project key or preview token and a signed-in app session (header or cookie). Signed-out callers get 401. Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Endpoints

Sign up

POST/__appbricx/auth/signup

Create an end-user account and sign in. Sets the appbricx_app_session cookie and returns the session token.

Auth
App key: Authorization: Bearer $APP_KEY

Body

NameTypeDescription
emailrequiredstringThe address.
passwordrequiredstringAt least 8 characters.
nameoptionalstringDisplay name (200 chars max).
personaoptionalstringA self-signup persona (see signup-options).

Response

When verification is required: { ok: true, pending: true, message }.

Response
{
  "ok": true,
  "token": "eyJhbGciOi…",
  "user": {
    "id": "u_42",
    "email": "ada@example.com",
    "name": "Ada",
    "isAdmin": false,
    "role": "customer",
    "orgId": null
  }
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
400INVALID_EMAILMalformed address.
400WEAK_PASSWORDUnder 8 characters.
403SIGNUP_DISABLEDSign-ups are closed.
403SIGNUP_INVITE_ONLYInvite-only app.
429(rate limit)Too many sign-ups from this address.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/signup" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "content-type: application/json" \
  -d '{"email":"ada@example.com","password":"correct horse battery","name":"Ada"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/signup`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "email": "ada@example.com",
    "password": "correct horse battery",
    "name": "Ada"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Relatedapp.auth.signUpusePlatformSession().signUp

Source: services/api/src/routes/app-auth.ts:309

Sign in

POST/__appbricx/auth/login

Password sign-in. Sets the session cookie and returns the token. Accounts with two-factor sign-in get a pending token to complete with /auth/mfa/verify.

Auth
App key: Authorization: Bearer $APP_KEY

Body

NameTypeDescription
emailrequiredstringThe address.
passwordrequiredstringThe password.

Response

Two-factor: { ok: true, needsMfa: true, pending, user }.

Response
{
  "ok": true,
  "token": "eyJhbGciOi…",
  "user": {
    "id": "u_42",
    "email": "ada@example.com",
    "name": "Ada",
    "role": "customer",
    "isAdmin": false,
    "orgId": null
  }
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
400INVALID_CREDENTIALSEmail or password missing.
401INVALID_CREDENTIALS"Incorrect email or password."
403ACCOUNT_DISABLEDThe account is disabled.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/login" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "content-type: application/json" \
  -d '{"email":"ada@example.com","password":"correct horse battery"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/login`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "email": "ada@example.com",
    "password": "correct horse battery"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Relatedapp.auth.signInPOST /__appbricx/auth/mfa/verify

Source: services/api/src/routes/app-auth.ts:538

Current user

GET/__appbricx/auth/me

The signed-in user from the session cookie or x-appbricx-app-session, or null.

Auth
App key: Authorization: Bearer $APP_KEY

Response

Response
{
  "ok": true,
  "user": {
    "id": "u_42",
    "email": "ada@example.com",
    "name": "Ada",
    "role": "customer",
    "isAdmin": false
  }
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
curl
curl "$APP_ORIGIN/__appbricx/auth/me" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/me`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

RelatedusePlatformSession().session

Source: services/api/src/routes/app-auth.ts:602

Sign-up options

GET/__appbricx/auth/signup-options

The signup policy and the personas a visitor may pick at sign-up.

Auth
App key: Authorization: Bearer $APP_KEY

Response

Response
{
  "ok": true,
  "signupPolicy": "open",
  "personas": [
    "customer"
  ]
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
curl
curl "$APP_ORIGIN/__appbricx/auth/signup-options" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/signup-options`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Relatedapp.auth.canSignUpusePlatformSession().discovery

Source: services/api/src/routes/app-auth.ts:619

Sign out

POST/__appbricx/auth/logout

Clear the session cookie.

Auth
App key: Authorization: Bearer $APP_KEY

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/logout" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/logout`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Relatedapp.auth.signOut

Source: services/api/src/routes/app-auth.ts:630

Request email verification

POST/__appbricx/auth/verify/request

Email a verification link. Constant response.

Auth
App key: Authorization: Bearer $APP_KEY

Body

NameTypeDescription
emailrequiredstringThe address.

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
400INVALID_EMAILMalformed address.
429(rate limit)Too many mail requests.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/verify/request" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "content-type: application/json" \
  -d '{"email":"ada@example.com"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/verify/request`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "email": "ada@example.com"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-auth.ts:736

Confirm email

GET/__appbricx/auth/verify/confirm

The emailed verification link. Redirects to the app with ?verified=1 when the app's origin is known, else answers JSON.

Auth
App key: Authorization: Bearer $APP_KEY

Query parameters

NameTypeDescription
tokenrequiredstringFrom the email.

Response

Response
{
  "ok": true,
  "verified": true
}

Errors

StatusCodeWhen
400MISSING_TOKENNo token.
400INVALID_TOKENExpired or forged.
400TOKEN_USEDAlready used.
curl
curl "$APP_ORIGIN/__appbricx/auth/verify/confirm?token=eyJ%E2%80%A6" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/verify/confirm?token=eyJ%E2%80%A6`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-auth.ts:757

Request password reset

POST/__appbricx/auth/reset/request

Email a reset link. Constant response whether or not the account exists.

Auth
App key: Authorization: Bearer $APP_KEY

Body

NameTypeDescription
emailrequiredstringThe address.

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
400INVALID_EMAILMalformed address.
429(rate limit)Too many mail requests.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/reset/request" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "content-type: application/json" \
  -d '{"email":"ada@example.com"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/reset/request`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "email": "ada@example.com"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Relatedapp.auth.requestReset

Source: services/api/src/routes/app-auth.ts:781

Set a new password

POST/__appbricx/auth/reset/confirm

Complete a reset with the emailed token.

Auth
App key: Authorization: Bearer $APP_KEY

Body

NameTypeDescription
tokenrequiredstringFrom the email.
newPasswordrequiredstringAt least 8 characters.

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
400MISSING_TOKENNo token.
400WEAK_PASSWORDUnder 8 characters.
400INVALID_TOKENExpired or forged.
400TOKEN_USEDAlready used.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/reset/confirm" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "content-type: application/json" \
  -d '{"token":"eyJ…","newPassword":"a much longer passphrase"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/reset/confirm`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "token": "eyJ…",
    "newPassword": "a much longer passphrase"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-auth.ts:798

Request a magic link

POST/__appbricx/auth/magic/request

Email a one-time sign-in link. Constant response.

Auth
App key: Authorization: Bearer $APP_KEY

Body

NameTypeDescription
emailrequiredstringThe address.

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
400INVALID_EMAILMalformed address.
429(rate limit)Too many mail requests.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/magic/request" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "content-type: application/json" \
  -d '{"email":"ada@example.com"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/magic/request`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "email": "ada@example.com"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-auth.ts:822

Use a magic link

GET/__appbricx/auth/magic/consume

The emailed link: signs the user in (sets the cookie) and redirects to the app with ?magic=1, else answers JSON with the session.

Auth
App key: Authorization: Bearer $APP_KEY

Query parameters

NameTypeDescription
tokenrequiredstringFrom the email.

Response

Response
{
  "ok": true,
  "token": "eyJhbGciOi…",
  "user": {
    "id": "u_42",
    "email": "ada@example.com",
    "name": "Ada",
    "isAdmin": false
  }
}

Errors

StatusCodeWhen
400MISSING_TOKENNo token.
400INVALID_TOKENExpired or forged.
400TOKEN_USEDAlready used.
400USER_NOT_FOUNDThe account is gone or disabled.
curl
curl "$APP_ORIGIN/__appbricx/auth/magic/consume?token=eyJ%E2%80%A6" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/magic/consume?token=eyJ%E2%80%A6`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-auth.ts:839

List users (admin)

GET/__appbricx/auth/users

The app's accounts with their roles. Backs the TeamAdmin block.

Auth
App key + admin session: Authorization: Bearer $APP_KEYx-appbricx-app-session: $ADMIN_SESSION

Query parameters

NameTypeDescription
limitoptionalnumberPage size.
offsetoptionalnumberRows to skip.

Response

Response
{
  "ok": true,
  "users": [
    {
      "id": "u_42",
      "email": "ada@example.com",
      "name": "Ada",
      "isAdmin": false,
      "disabled": false,
      "role": "customer"
    }
  ]
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
403none"Admin required".
curl
curl "$APP_ORIGIN/__appbricx/auth/users?limit=200" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $ADMIN_SESSION"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/users?limit=200`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": ADMIN_SESSION,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-auth.ts:881

Assignable roles (admin)

GET/__appbricx/auth/roles

The personas an admin may hand out.

Auth
App key + admin session: Authorization: Bearer $APP_KEYx-appbricx-app-session: $ADMIN_SESSION

Response

Response
{
  "ok": true,
  "roles": [
    {
      "id": "staff",
      "label": "Staff",
      "kind": "staff"
    },
    {
      "id": "customer",
      "label": "Customer",
      "kind": "consumer"
    }
  ]
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
403none"Admin required".
curl
curl "$APP_ORIGIN/__appbricx/auth/roles" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $ADMIN_SESSION"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/roles`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": ADMIN_SESSION,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-auth.ts:980

Invite a user (admin)

POST/__appbricx/auth/users

Create an account with a role.

Auth
App key + admin session: Authorization: Bearer $APP_KEYx-appbricx-app-session: $ADMIN_SESSION

Body

NameTypeDescription
emailrequiredstringThe address.
passwordrequiredstringAt least 8 characters.
nameoptionalstringDisplay name.
roleoptionalstringOne of GET /auth/roles.
isAdminoptionalbooleanGrant the admin flag.

Response

201 Created. warning is set when the account exists but its role couldn't be recorded.

Response
{
  "ok": true,
  "user": {
    "id": "u_43",
    "email": "sam@example.com",
    "name": "Sam",
    "role": "staff"
  }
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
403none"Admin required".
400INVALID_EMAILMalformed address.
400WEAK_PASSWORDUnder 8 characters.
400INVALID_ROLENot an assignable role.
409EMAIL_TAKENAn account exists.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/users" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $ADMIN_SESSION" \
  -H "content-type: application/json" \
  -d '{"email":"sam@example.com","password":"temporary-pass-123","name":"Sam","role":"staff"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/users`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": ADMIN_SESSION,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "email": "sam@example.com",
    "password": "temporary-pass-123",
    "name": "Sam",
    "role": "staff"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-auth.ts:991

Change a user (admin)

PATCH/__appbricx/auth/users/:id

Change name, email, role, admin flag or disabled state.

Auth
App key + admin session: Authorization: Bearer $APP_KEYx-appbricx-app-session: $ADMIN_SESSION

Path parameters

NameTypeDescription
:idrequiredstringThe user id.

Body

NameTypeDescription
nameoptionalstringDisplay name.
emailoptionalstringAddress.
roleoptionalstringA persona id.
isAdminoptionalbooleanAdmin flag.
disabledoptionalbooleanDisable the account.

Response

Response
{
  "ok": true,
  "user": {
    "id": "u_43",
    "email": "sam@example.com",
    "name": "Sam",
    "role": "manager",
    "disabled": false
  }
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
403none"Admin required".
400SELF_LOCKOUTYou can't disable or demote yourself.
400INVALID_ROLENot an assignable role.
404NOT_FOUNDNo such user.
curl
curl -X PATCH "$APP_ORIGIN/__appbricx/auth/users/u_43" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $ADMIN_SESSION" \
  -H "content-type: application/json" \
  -d '{"role":"manager"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/users/u_43`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": ADMIN_SESSION,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "role": "manager"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-auth.ts:1046

Set-password page

GET/__appbricx/auth/set-password

An HTML form an invited user opens from their email to choose a password (links work once, for 1 hour). Reset links that land on the app as /?reset=<token> are forwarded here.

Auth
App key: Authorization: Bearer $APP_KEY

Query parameters

NameTypeDescription
tokenrequiredstringFrom the email.

Response

text/html.

Response
<!doctype html> … Set your password …

Errors

StatusCodeWhen
400(html)"This link has expired".
curl
curl "$APP_ORIGIN/__appbricx/auth/set-password?token=eyJ%E2%80%A6" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/set-password?token=eyJ%E2%80%A6`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const text = await res.text();

Source: services/api/src/routes/app-auth.ts:1185

Submit set-password form

POST/__appbricx/auth/set-password

The form post behind the set-password page (application/x-www-form-urlencoded: token, password, confirm).

Auth
App key: Authorization: Bearer $APP_KEY

Body

NameTypeDescription
tokenrequiredstringFrom the link.
passwordrequiredstringAt least 8 characters.
confirmrequiredstringSame again.

Response

text/html.

Response
<!doctype html> … Password saved …

Errors

StatusCodeWhen
400(html)Expired link, password under 8 characters, passwords that don't match, or a used link.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/set-password" \
  -H "Authorization: Bearer $APP_KEY" \
  --data "token=eyJ…&password=a+much+longer+passphrase&confirm=a+much+longer+passphrase"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/set-password`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
  body: new URLSearchParams("token=eyJ…&password=a+much+longer+passphrase&confirm=a+much+longer+passphrase"),
});
const text = await res.text();

Source: services/api/src/routes/app-auth.ts:1195

Social providers

GET/__appbricx/auth/oauth/providers

The social sign-in providers turned on for this app.

Auth
App key: Authorization: Bearer $APP_KEY

Response

Response
{
  "ok": true,
  "providers": [
    {
      "name": "google",
      "label": "Google"
    }
  ]
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
curl
curl "$APP_ORIGIN/__appbricx/auth/oauth/providers" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/oauth/providers`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Relatedapp.auth.providers

Source: services/api/src/routes/app-oauth.ts:165

Start social sign-in

GET/__appbricx/auth/oauth/:provider/start

Redirect to the provider (302), or return the URL with json=1. The user returns to returnTo signed in, with ?oauth=1 (or ?oauth_error=<code>).

Auth
App key: Authorization: Bearer $APP_KEY

Path parameters

NameTypeDescription
:providerrequiredstring"google" or "github".

Query parameters

NameTypeDescription
returnTooptionalstringWhere to land afterwards: a full URL on one of the app's hosts, or a path.
jsonoptional"1"Answer { ok, url } instead of redirecting.

Response

Response
{
  "ok": true,
  "url": "https://accounts.google.com/o/oauth2/v2/auth?client_id=…"
}

Errors

StatusCodeWhen
401UNAUTHORIZEDMissing or invalid Authorization bearer ("Missing Authorization: Bearer <token>", "Invalid API key").
404UNKNOWN_PROVIDERNot a supported provider.
400PROVIDER_NOT_CONFIGUREDNot turned on for this app.
400ORIGIN_NOT_ALLOWEDreturnTo isn't one of the app's hosts.
curl
curl "$APP_ORIGIN/__appbricx/auth/oauth/google/start?json=1&returnTo=https%3A%2F%2Ftablefor.apps.appbricx.com%2F" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/oauth/google/start?json=1&returnTo=https%3A%2F%2Ftablefor.apps.appbricx.com%2F`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Relatedapp.auth.startProvider

Source: services/api/src/routes/app-oauth.ts:180

Social sign-in callback

GET/__appbricx/auth/oauth/:provider/callback

The redirect URI to register with your own OAuth client (https://<app host>/__appbricx/auth/oauth/<provider>/callback). Exchanges the code, signs the user in and redirects back.

Auth
App key: Authorization: Bearer $APP_KEY

Path parameters

NameTypeDescription
:providerrequiredstring"google" or "github".

Query parameters

NameTypeDescription
coderequiredstringFrom the provider.
staterequiredstringThe signed state.

Response

Response
302 Found → <returnTo>?oauth=1

Errors

StatusCodeWhen
400INVALID_STATEExpired or forged state.
400PROVIDER_MISMATCHState is for another provider.
400MISSING_CODENo code.
curl
curl "$APP_ORIGIN/__appbricx/auth/oauth/github/callback?code=abc&state=eyJ%E2%80%A6" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/oauth/github/callback?code=abc&state=eyJ%E2%80%A6`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const text = await res.text();

Source: services/api/src/routes/app-oauth.ts:231

Complete shared sign-in

GET/__appbricx/auth/oauth/complete

Last hop of Appbricx's shared Google sign-in: redeems a one-time code on the app's own host, sets the session cookie and redirects with ?oauth=1.

Auth
App key: Authorization: Bearer $APP_KEY

Query parameters

NameTypeDescription
coderequiredstringThe one-time handoff code.

Response

Response
302 Found → <returnUrl>?oauth=1

Errors

StatusCodeWhen
400INVALID_OR_EXPIRED_CODEUsed, expired, or redeemed on another host.
curl
curl "$APP_ORIGIN/__appbricx/auth/oauth/complete?code=h_9f2%E2%80%A6" \
  -H "Authorization: Bearer $APP_KEY"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/oauth/complete?code=h_9f2%E2%80%A6`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
  },
});
const text = await res.text();

Source: services/api/src/routes/app-oauth.ts:459

Configure a provider (admin)

POST/__appbricx/auth/oauth/config

Store your own OAuth client for a provider.

Auth
App key + admin session: Authorization: Bearer $APP_KEYx-appbricx-app-session: $ADMIN_SESSION

Body

NameTypeDescription
providerrequiredstring"google" or "github".
client_idrequiredstringYour client id.
client_secretrequiredstringYour client secret.
enabledoptionalbooleanDefault true.

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
401AUTH_REQUIREDNo session.
401INVALID_SESSIONBad session.
403ADMIN_REQUIREDNot an admin.
400UNKNOWN_PROVIDERUnsupported provider.
400MISSING_CREDENTIALSclient_id or client_secret missing.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/oauth/config" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $ADMIN_SESSION" \
  -H "content-type: application/json" \
  -d '{"provider":"github","client_id":"Iv1.abc","client_secret":"…","enabled":true}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/oauth/config`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": ADMIN_SESSION,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "provider": "github",
    "client_id": "Iv1.abc",
    "client_secret": "…",
    "enabled": true
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

RelatedMCP sign_in_providers

Source: services/api/src/routes/app-oauth.ts:472

Remove a provider config (admin)

DELETE/__appbricx/auth/oauth/config/:provider

Delete your own OAuth client for a provider.

Auth
App key + admin session: Authorization: Bearer $APP_KEYx-appbricx-app-session: $ADMIN_SESSION

Path parameters

NameTypeDescription
:providerrequiredstring"google" or "github".

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
401AUTH_REQUIREDNo session.
403ADMIN_REQUIREDNot an admin.
curl
curl -X DELETE "$APP_ORIGIN/__appbricx/auth/oauth/config/github" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $ADMIN_SESSION"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/oauth/config/github`, {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": ADMIN_SESSION,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-oauth.ts:511

Start two-factor enrolment

POST/__appbricx/auth/mfa/enroll/start

Create a TOTP secret for the signed-in user. Show otpauth_url as a QR code.

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Response

Response
{
  "ok": true,
  "secret": "JBSWY3DPEHPK3PXP",
  "otpauth_url": "otpauth://totp/Tablefor:ada%40example.com?secret=JBSW…"
}

Errors

StatusCodeWhen
401noneNot signed in.
400ALREADY_ENROLLEDTwo-factor is already on.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/mfa/enroll/start" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION" \
  -H "content-type: application/json" \
  -d '{}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/mfa/enroll/start`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
    "content-type": "application/json",
  },
  body: JSON.stringify({}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-mfa.ts:211

Confirm two-factor enrolment

POST/__appbricx/auth/mfa/enroll/confirm

Prove the authenticator works with a 6-digit code. Returns one-time backup codes (shown once).

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Body

NameTypeDescription
coderequiredstringThe current 6-digit code.

Response

Response
{
  "ok": true,
  "backupCodes": [
    "8f3k-2m9d",
    "…"
  ]
}

Errors

StatusCodeWhen
400NO_PENDING_ENROLLMENTCall enroll/start first.
400INVALID_CODEWrong code.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/mfa/enroll/confirm" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION" \
  -H "content-type: application/json" \
  -d '{"code":"123456"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/mfa/enroll/confirm`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "code": "123456"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-mfa.ts:236

Complete a two-factor sign-in

POST/__appbricx/auth/mfa/verify

Exchange the pending token from /auth/login plus a 6-digit or backup code for a session.

Auth
App key: Authorization: Bearer $APP_KEY

Body

NameTypeDescription
pendingrequiredstringFrom the login answer.
coderequiredstringTOTP or backup code.

Response

Response
{
  "ok": true,
  "token": "eyJhbGciOi…",
  "user": {
    "id": "u_42",
    "email": "ada@example.com",
    "name": "Ada",
    "isAdmin": false
  },
  "verifiedVia": "totp"
}

Errors

StatusCodeWhen
400INVALID_INPUTpending or code missing.
400INVALID_PENDINGExpired pending token.
400INVALID_CODEWrong code.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/mfa/verify" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "content-type: application/json" \
  -d '{"pending":"eyJ…","code":"123456"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/mfa/verify`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "pending": "eyJ…",
    "code": "123456"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-mfa.ts:269

Turn off two-factor

POST/__appbricx/auth/mfa/disable

Disable two-factor for the signed-in user (needs a current code).

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Body

NameTypeDescription
coderequiredstringTOTP or backup code.

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
400INVALID_CODEWrong code.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/mfa/disable" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION" \
  -H "content-type: application/json" \
  -d '{"code":"123456"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/mfa/disable`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "code": "123456"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-mfa.ts:325

New backup codes

POST/__appbricx/auth/mfa/backup-codes/regenerate

Replace the backup codes (needs a current code).

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Body

NameTypeDescription
coderequiredstringTOTP or backup code.

Response

Response
{
  "ok": true,
  "backupCodes": [
    "2m9d-8f3k",
    "…"
  ]
}

Errors

StatusCodeWhen
400INVALID_CODEWrong code.
curl
curl -X POST "$APP_ORIGIN/__appbricx/auth/mfa/backup-codes/regenerate" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION" \
  -H "content-type: application/json" \
  -d '{"code":"123456"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/auth/mfa/backup-codes/regenerate`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "code": "123456"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-mfa.ts:343

Invite to the organisation

POST/__appbricx/orgs/invite

Email an invitation to join the caller's active organisation.

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Body

NameTypeDescription
emailrequiredstringInvitee address.
roleoptional"owner" | "admin" | "member"Default member.

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
400NO_ORGThe session has no active organisation.
403ADMIN_REQUIREDOnly owners and admins invite.
400INVALID_EMAILMalformed address.
400INVALID_ROLEUnknown role.
curl
curl -X POST "$APP_ORIGIN/__appbricx/orgs/invite" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION" \
  -H "content-type: application/json" \
  -d '{"email":"sam@example.com","role":"member"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/orgs/invite`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "email": "sam@example.com",
    "role": "member"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-invites.ts:126

Accept an invitation

GET/__appbricx/orgs/invite/accept

The emailed link: joins the organisation and redirects to the app with ?joined=1.

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Query parameters

NameTypeDescription
tokenrequiredstringFrom the email.

Response

Response
302 Found → /?joined=1

Errors

StatusCodeWhen
400MISSING_TOKENNo token.
400INVALID_TOKENUnknown token.
400TOKEN_USEDAlready accepted.
400TOKEN_EXPIREDExpired.
403EMAIL_MISMATCHSigned in as another address.
curl
curl "$APP_ORIGIN/__appbricx/orgs/invite/accept?token=inv_%E2%80%A6" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/orgs/invite/accept?token=inv_%E2%80%A6`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
  },
});
const text = await res.text();

Source: services/api/src/routes/app-invites.ts:185

Organisation members

GET/__appbricx/orgs/members

Members of the caller's active organisation.

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Response

Response
{
  "ok": true,
  "members": [
    {
      "userId": "u_42",
      "email": "ada@example.com",
      "role": "owner"
    }
  ]
}

Errors

StatusCodeWhen
403NOT_A_MEMBERNot a member of the active organisation.
curl
curl "$APP_ORIGIN/__appbricx/orgs/members" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/orgs/members`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-invites.ts:244

Change a member's role

PATCH/__appbricx/orgs/members/:userId

Set a member's organisation role.

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Path parameters

NameTypeDescription
:userIdrequiredstringThe member's user id.

Body

NameTypeDescription
rolerequired"owner" | "admin" | "member"The new role.

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
400NO_ORGNo active organisation.
403ADMIN_REQUIREDOwners and admins only.
400INVALID_ROLEUnknown role.
curl
curl -X PATCH "$APP_ORIGIN/__appbricx/orgs/members/u_43" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION" \
  -H "content-type: application/json" \
  -d '{"role":"admin"}'
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/orgs/members/u_43`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "role": "admin"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-invites.ts:274

Remove a member

DELETE/__appbricx/orgs/members/:userId

Remove a member from the organisation.

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Path parameters

NameTypeDescription
:userIdrequiredstringThe member's user id.

Response

Response
{
  "ok": true
}

Errors

StatusCodeWhen
400NO_ORGNo active organisation.
403ADMIN_REQUIREDOwners and admins only.
400LAST_OWNERCan't remove the last owner.
curl
curl -X DELETE "$APP_ORIGIN/__appbricx/orgs/members/u_43" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/orgs/members/u_43`, {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-invites.ts:299

Current organisation

GET/__appbricx/orgs/current

The caller's active organisation and their role in it, or null.

Auth
App key + signed-in user: Authorization: Bearer $APP_KEYx-appbricx-app-session: $APP_SESSION

Response

Response
{
  "ok": true,
  "org": {
    "id": "org_7",
    "name": "Acme",
    "createdAt": "2026-09-01T10:00:00Z",
    "role": "owner"
  }
}

Errors

StatusCodeWhen
401noneNot signed in.
curl
curl "$APP_ORIGIN/__appbricx/orgs/current" \
  -H "Authorization: Bearer $APP_KEY" \
  -H "x-appbricx-app-session: $APP_SESSION"
fetch
const res = await fetch(`${APP_ORIGIN}/__appbricx/orgs/current`, {
  headers: {
    Authorization: `Bearer ${APP_KEY}`,
    "x-appbricx-app-session": APP_SESSION,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? data.error ?? res.statusText);

Source: services/api/src/routes/app-invites.ts:333

On this page

  1. Authentication
  2. Endpoints
  3. POST /auth/signup
  4. POST /auth/login
  5. GET /auth/me
  6. GET /auth/signup-options
  7. POST /auth/logout
  8. POST /auth/verify/request
  9. GET /auth/verify/confirm
  10. POST /auth/reset/request
  11. POST /auth/reset/confirm
  12. POST /auth/magic/request
  13. GET /auth/magic/consume
  14. GET /auth/users
  15. GET /auth/roles
  16. POST /auth/users
  17. PATCH /auth/users/:id
  18. GET /auth/set-password
  19. POST /auth/set-password
  20. GET /auth/oauth/providers
  21. GET /auth/oauth/:provider/start
  22. GET /auth/oauth/:provider/callback
  23. GET /auth/oauth/complete
  24. POST /auth/oauth/config
  25. DELETE /auth/oauth/config/:provider
  26. POST /auth/mfa/enroll/start
  27. POST /auth/mfa/enroll/confirm
  28. POST /auth/mfa/verify
  29. POST /auth/mfa/disable
  30. POST /auth/mfa/backup-codes/regenerate
  31. POST /orgs/invite
  32. GET /orgs/invite/accept
  33. GET /orgs/members
  34. PATCH /orgs/members/:userId
  35. DELETE /orgs/members/:userId
  36. GET /orgs/current
Appbricx

The backend, runtime and hosting for apps your coding agent builds. On our cloud, in yours, or on your own servers.

Features

Coding agents and MCPVerify testingBuilder consolePostgres databaseData APIRealtimeRow-level securitySign-in and authPaymentsWorkflows and jobsAI in your appIntegrationsBackups and rollbackHosting and domainsSelf-host and exportAll features

Product

ShowcasePricingFor coding agentsTemplatesPrivate cloudBring your own AI keyFAQ

Compare

Appbricx vs SupabaseLovable alternativeBolt alternativeFor freelancersFor agencies

Resources

DocumentationReferenceBlogSupportContact

© 2026 Appbricx. All rights reserved.

TermsPrivacyCookiesAcceptable Use