Authentication

Endpoints for account registration, login, OAuth, password management, and session lifecycle. All request and response bodies are JSON.

Response Envelope

Every endpoint returns the same envelope. Successful responses set success: true and carry the payload under data:

{
  "success": true,
  "data": { /* endpoint-specific payload */ }
}

Failed responses set success: false and carry a machine-readable code and a human-readable message under error:

{
  "success": false,
  "error": {
    "code": "AUTH_INVALID_CREDENTIALS",
    "message": "Invalid email or password"
  }
}

Register

POST /v1/auth/register

Create a new account with an email and password. This endpoint provisions the account and dispatches a verification email; it does not return access or refresh tokens. To prevent account enumeration, the response is the same generic message whether or not the email is already registered. Treat the generic message as success and direct the user to check their inbox. displayName and orgId are optional.

Request Body

{
  "email": "user@example.com",
  "password": "your-password",
  "displayName": "Jane Smith"
}

Response

{
  "success": true,
  "data": {
    "message": "If this email is not already registered, check your inbox for a verification link."
  }
}

Passwords must satisfy the platform policy (minimum length plus at least one uppercase letter, lowercase letter, digit, and special character). A password that fails policy, or one that appears in a known breach corpus, is rejected with a 400 and a specific code such as WEAK_PASSWORD or BREACHED_PASSWORD. These password errors are safe to surface because they do not reveal whether the email exists.

Email-Verified Sign-Up

Backbuild also offers a code-based sign-up flow. Start it with /v1/auth/initiate to email a one-time verification code and obtain a flow_id, optionally check the code with /v1/auth/registration/validate, then redeem it and set the password with /v1/auth/complete-registration.

Initiate

POST /v1/auth/initiate

Emails a one-time verification code and returns a flow_id. The response is intentionally uniform regardless of whether the email belongs to a new or returning user, so it never reveals account existence. identifier_type is optional and defaults to email.

Request Body

{
  "identifier": "user@example.com"
}

Response

{
  "success": true,
  "data": {
    "flow_id": "...",
    "message": "Check your email for a verification code"
  }
}

Validate Code

POST /v1/auth/registration/validate

Optionally verify that a code is valid for a flow before completing registration (for example, to advance a multi-step form). It does not issue tokens. An invalid code returns 401; an expired or consumed flow returns 410.

Request Body

{
  "flow_id": "...",
  "identifier": "user@example.com",
  "verification_code": "123456"
}

Complete Registration

POST /v1/auth/complete-registration

Redeems the emailed verification code, sets the account password, and (when no further factor is required) returns the session tokens. display_name is optional.

Request Body

{
  "flow_id": "...",
  "identifier": "user@example.com",
  "verification_code": "123456",
  "password": "your-password",
  "display_name": "Jane Smith"
}

Response

{
  "success": true,
  "data": {
    "tokens": {
      "access_token": "eyJ...",
      "refresh_token": "eyJ...",
      "expires_in": 900,
      "token_type": "Bearer"
    },
    "user_id": "..."
  }
}

If the account requires multi-factor authentication, the response instead carries mfa_required with an mfa_challenge_token (and the available mfa_methods), or mfa_enrollment_required with an mfa_enrollment_token, rather than session tokens. Complete the MFA step before tokens are issued.

Login

POST /v1/auth/login

Authenticate with email and password. On success, returns access and refresh tokens. An invalid email or password returns a generic 401 with code AUTH_INVALID_CREDENTIALS; the response never reveals whether the email exists or whether the account is locked.

Request Body

{
  "email": "user@example.com",
  "password": "your-password"
}

Response

{
  "success": true,
  "data": {
    "success": true,
    "userId": "...",
    "email": "user@example.com",
    "displayName": "Jane Smith",
    "orgId": "...",
    "sessionId": "...",
    "accessToken": "eyJ...",
    "refreshToken": "eyJ...",
    "accessTokenExpiresAt": "...",
    "refreshTokenExpiresAt": "..."
  }
}

When the account has MFA enabled or pending enrollment, the response instead carries mfaRequired (with mfaChallengeToken and mfaMethods) or mfaEnrollmentRequired (with mfaEnrollmentToken) rather than access and refresh tokens.

Refresh Token

POST /v1/auth/refresh

Exchange a refresh token for a fresh access token. Refresh tokens are rotated, so each call invalidates the refresh token it consumes.

Request Body

{
  "refreshToken": "eyJ..."
}

The response carries the same shape as the login response: a fresh accessToken and rotated refreshToken. An invalid or expired refresh token returns 401 with code AUTH_REFRESH_TOKEN_INVALID.

Logout

POST /v1/auth/logout

Revoke the current session. Requires a valid access token.

POST /v1/auth/logout-all

Revoke every active session for the authenticated user across all devices. Requires a valid access token.

Google OAuth

POST /v1/auth/google-oauth

Sign in or sign up with a Google authorization code. Exchange the code obtained from Google together with the redirectUri used to obtain it. If the Google account email matches an existing password account that is not yet linked, the response indicates that account linking is required (googleLinkRequired, with a short-lived linkToken). As with email login, the response may instead carry an MFA challenge or enrollment requirement.

Request Body

{
  "code": "4/0Adeu5...",
  "redirectUri": "https://app.backbuild.ai/auth/google/oauth"
}
POST /v1/auth/google-oauth/confirm-link

Consent to linking the Google identity to the existing matching account. Requires the caller to be authenticated as that local account and to present the linkToken returned by the prior /v1/auth/google-oauth call.

One-Time Codes

POST /v1/auth/otp/create

Request a one-time code by email. purpose is one of login, verify_email, or reset_password. The response is always a generic message so it never reveals whether the email is registered.

Request Body

{
  "email": "user@example.com",
  "purpose": "login"
}
POST /v1/auth/otp/verify

Verify a one-time code. purpose must match the value used to create the code. An invalid or expired code returns 401.

Request Body

{
  "email": "user@example.com",
  "code": "123456",
  "purpose": "login"
}

Password Reset

Request Reset

POST /v1/auth/password/reset/request

Request Body

{
  "identifier": "user@example.com"
}

Sends a password reset code by email. The response is a fixed generic message regardless of whether an account exists for the address, so it cannot be used to enumerate accounts.

Complete Reset

POST /v1/auth/password/reset

Request Body

{
  "email": "user@example.com",
  "code": "123456",
  "new_password": "new-secure-password"
}

Verifies the emailed code and sets a new password. The new password must satisfy the platform password policy.

Change Password

POST /v1/auth/password/change

Change the password for the authenticated user (also used by OAuth-only users to set a password for the first time). Requires a valid access token and a recently completed multi-factor step; if the session’s MFA is stale the request is rejected and the client must re-complete an MFA ceremony first.

Request Body

{
  "old_password": "current-password",
  "new_password": "new-secure-password"
}

Passwordless (Passkey) Sign-In

POST /v1/auth/webauthn/assertion/options

Begin a passwordless WebAuthn sign-in. Returns the assertion request options and an assertion_nonce to echo back on verify.

POST /v1/auth/webauthn/assertion/verify

Complete the WebAuthn ceremony by submitting the authenticator credential together with the assertion_nonce. On success, and when passkey-as-primary sign-in is enabled for the account, the response issues access and refresh tokens just like a password login.

Device Authorization (CLI & Agents)

Command-line tools and agents authenticate with an OAuth 2.0 Authorization Code flow with PKCE (RFC 7636, S256 only). The tool opens an authorization URL in a browser; once the user approves in their logged-in session, the tool exchanges the resulting code (with its PKCE verifier) for tokens.

POST /v1/auth/device/authorize

Start the flow. Send a localhost redirect_uri, a PKCE code_challenge (S256), and an opaque state. Returns an authorization URL for the user to open in a browser.

POST /v1/auth/device/complete

Called by the logged-in browser session (authenticated) to approve the pending device request.

POST /v1/auth/device/token

Exchange the authorization code together with the PKCE code_verifier for access and refresh tokens after the user completes browser-based auth.

Handling Challenges & Errors

Beyond per-endpoint validation errors, integrators should handle the following responses, which can occur on the identity endpoints above.

Proof-of-Human Challenge (401)

Identity endpoints (register, complete-registration, initiate, login, one-time-code creation, and password-reset requests) may require a Cloudflare Turnstile proof-of-human token; automated or high-volume traffic is more likely to be challenged. When a token is required and is missing or invalid, the API responds with HTTP 401 and a flat body:

{
  "error": "turnstile_required",
  "message": "This endpoint requires a Cloudflare Turnstile challenge"
}

On this response, render a Cloudflare Turnstile widget, obtain a token, and resubmit the same request with the token supplied either as the request header cf-turnstile-response or as the JSON body field turnstileToken (the body fields turnstile_token and cf-turnstile-response are also accepted).

Rate Limiting (429)

Requests may be rate limited and receive HTTP 429. When a Retry-After header is present, honor it and back off for the indicated number of seconds before retrying. Build clients to back off and retry rather than to loop immediately.

Region Restrictions (451)

Requests originating from embargoed or sanctioned regions receive HTTP 451 for sanctions-compliance reasons. This is not retryable from the affected region.

Access Denied (403)

Requests from blocked sources (blacklisted IPs and anonymizing VPN exit nodes) receive HTTP 403. Browser calls are additionally governed by a CORS allowlist, so only approved web origins can read API responses from a browser context.

Token Format

Access tokens are short-lived bearer tokens (presented as Authorization: Bearer <token>); refresh tokens have a longer lifetime and are rotated, so each refresh invalidates the previous refresh token. Tokens are scoped to the user, the active organization, and that organization’s role. Use accessTokenExpiresAt and refreshTokenExpiresAt (or the expires_in seconds returned by complete-registration) to know when to refresh.