---
title: "Build a complete headless SaaS"
section: "Developer Platform"
canonical_url: "https://support.buildwithhq.com/api/headless-saas.html"
reviewed_at: "2026-09-14"
authority: Official
---

# Build a complete headless SaaS

Use every promoted first-class module from your own React, mobile, desktop, or legacy interface without exposing an API credential to the browser.

## In brief

- **Public contract:** 369 operations
- **First-class modules:** 274 typed operations
- **Browser secrets:** None

A headless SaaS keeps your own interface while BuildWithHQ supplies the governed application backend. Developer Kit 1.20.65 exposes all 274 promoted first-class module operations plus tenant account, user, role/location assignment, delegated-token, revocation, and DeepSeek Harness contracts. You can build the complete catalog without calling an internal tenant URL or connecting React to SQL.

[Download Developer Kit 1.20.65](../downloads/BuildWithHQ-API43-Developer-Kit-1.20.65.zip) [Browse the OpenAPI contract](versioning-and-openapi.html) [Create an app credential](authentication.html)

## The production shape

```
Browser / mobile / desktop UI
        | your session + CSRF
        v
Your trusted server or narrow BFF
        | app-bound BuildWithHQ credential
        v
https://api.buildwithhq.com/v1/apps/{saasAppId}/...
        | server-derived app + user + DataRole + location scope
        v
Only the authorized, paginated result returns to your UI
```

The app credential is bound to one SaaS app. Your trusted server uses it to manage that app's tenant accounts and users, then exchanges its own user identity for a short-lived delegated token. Every module request executes the same secured service contract as the native application. The URL chooses a target; it never proves authority.

The kit-root `TENANT-PROVISIONING-AND-AUTHENTICATION.md` guide maps both supported provisioning paths. Signed-in SaaS builders can administer organizations in the Builder Console, while external servers use the published `bwk_live_` tenant-account and tenant-user operations. Builder product routes intentionally are not app-credential routes. Their separate `openapi/platform-owner.json` contract and `BuildWithHQPlatformClient` use the current verified Builder session.

## Connect a marketing-site signup

Your public pricing or signup page posts to your own authenticated, rate-limited backend-for-frontend. That server verifies the email and identity-provider subject, then calls `createTenantAccount` with its protected live-app credential. Never place a `bwk_live_` credential in browser JavaScript, a public form, or a mobile bundle, and never send a raw password to BuildWithHQ.

The one idempotent call creates the top-level customer organization, first owner, protected owner role, up to five initial DataRoles, and up to five initial Locations. The returned key-to-ID maps can seed later user assignments. Because this is the same routed AppAccount contract used by the consoles, the new customer appears immediately under the app's **Organizations** directory and the owner sees the in-SaaS **Account administration** area. Additional roles and locations can be added later through normal administration.

[Follow the complete customer-organization website signup guide.](customer-organization-website-signup.html)

## Act as the real end user

1. Create or resolve an AppAccount with your stable external organization ID.
2. Create a passwordless TenantUser and assign only DataRoles and Locations from that account.
3. Exchange the user's external subject for a 2–15 minute `bwu_live_` token from your trusted server.
4. Give only that short-lived token to that user's browser or mobile client. It cannot manage identities or mint tokens.
5. Revoke it explicitly or deactivate the user. Deactivation revokes every active delegated token for that app/account/user.

```
const account = await api.createTenantAccount({
  providerKey: "your-auth", externalAccountId: organization.id,
  accountName: organization.name, primaryEmail: organization.billingEmail,
  ownerExternalSubject: currentUser.id, ownerEmail: currentUser.email,
  ownerDisplayName: currentUser.name,
  dataRoles: [{
    key: "member", name: "Member", canRead: true,
    canReadRecords: true, permissionKeys: ["records.read"],
  }],
  locations: [{
    key: "head-office", name: "Head office",
    timeZone: "America/Los_Angeles",
  }],
});

const delegated = await api.exchangeDelegatedUserToken({
  providerKey: "your-auth", externalAccountId: organization.id,
  externalSubject: currentUser.id,
  scopes: ["modules.contacts.read", "modules.work-orders.read"],
  lifetimeMinutes: 10,
});
```

Note

Zero UserLocationXref rows is the explicit all-locations contract. Send locationScopeMode: "all" deliberately; for "selected", send at least one reviewed location ID. An omitted or empty update never silently expands access.

Management scopes are `tenant-accounts.read/write`, `tenant-users.read/write`, and `tenant-users.tokens.issue`. Identity mutations and token issuance are written to the SaaS immutable Logs database with API-client and represented-user context.

## Embed the published page body or the complete page

Create a delegated token with `runtime.pages.read`. `BuildWithHQRuntimeClient.getPageBody(pageKey)` returns the registered components and page body without BuildWithHQ menus or shell, so the existing product keeps its own navigation. Page design tokens are omitted by default and can be explicitly included. `getPageBootstrap(pageKey)` returns the complete shell, menus, components, and page when the host wants the full BuildWithHQ experience.

The kit carries one compiled local-only `packages/page-runtime` library. React and Puck install it through locked `file:` dependencies, so `npm ci` downloads their declared third-party dependencies without publishing BuildWithHQ code or requiring an npm organization. The library parses presentation data; the VB Developer API remains the secured HTTP data and authorization boundary.

For blocks with registered data sources, add `runtime.bindings.read` and call `resolveBindings` with at most 24 unique keys. All three calls require the short-lived `bwu_live_` credential and reuse the hosted runtime's user entitlement, DataRole, Location, record filtering, tenant routing, stored procedures, and audit.

## Copy the server-side TypeScript start

```
import { BuildWithHQModuleClient } from "./sdk/BuildWithHQConnector.js";

const api = new BuildWithHQModuleClient({
  saasAppId: process.env.BWHQ_SAAS_APP_ID!,
  credential: process.env.BWHQ_API_CREDENTIAL!,
});

export async function contactsPage(search = "") {
  return api.contactsList({ search, pageNumber: 1, pageSize: 50 });
}

export async function workOrdersPage() {
  return api.workOrdersList({ pageNumber: 1, pageSize: 50 });
}
```

Important

Run this code on a trusted server only. Never put BWHQ_API_CREDENTIAL in VITE_*, NEXT_PUBLIC_*, page JSON, browser storage, a mobile bundle, logs, prompts, or source control.

## What the 1.20.65 kit gives you

- **All 369 public operations** in semantically identical generated JSON and YAML OpenAPI files.
- **All 274 first-class module operations** as typed methods in `sdk/modules.generated.ts`, exported by `BuildWithHQConnector.ts`.
- **Exact required scopes on every operation** in `catalog/operations.json`, with module rate classes and aliases in the sanitized `catalog/module-surface.json`.
- **Complete request and response DTOs**, including lists, detail reads, writes, files, searches, ticketing, approvals, and module-specific state changes.
- **React presentation sources** for professional dashboards and native Builder pages, while leaving authentication and mutations on the trusted side of the boundary.
- **Machine-readable coverage** whose two SDK manifests must equal OpenAPI exactly before the ZIP builds.

## Builder names and API names are explicitly joined

`catalog/capabilities.json` records each Builder feature slug beside its Developer API module key, public base path, and scopes. For example, the Builder feature `record-relations` uses the `record-graph` API module, `knowledge-core` uses `knowledge-articles`, and `dynamic-records` is served below `/modules/dynamic`. The React gallery displays both identities so developers do not infer route names from marketing labels.

## Native Builder and headless are two views of the same system

Builders save registered React blocks and pages in BuildWithHQ; those blocks use a signed-in tenant session. Headless developers use the generated server SDK and paint the returned data in their own React or other client. Both paths reach the same module services and secured database contracts. A developer can prototype a presentation from the kit, then either publish it as a Builder component or host it in a headless interface—the identity mechanism changes, but the business contracts and server-side data scope do not.

## Go-live checks

1. Issue a least-privilege credential with only the module read/write scopes the server needs.
2. Keep list/search calls bounded and preserve server pagination; never download a tenant graph to render one row.
3. Handle 401, 403, 404, 409, 429, and 5xx distinctly and retain the correlation ID.
4. Test a valid record, a foreign-tenant record, and a random nonexistent ID. Foreign and nonexistent responses must remain indistinguishable.
5. Exercise optimistic concurrency, file scanning, retry/idempotency, empty states, mobile layout, keyboard access, and service recovery.
6. Generate clients from the checked-in OpenAPI version and pin the Developer Kit SHA-256 for the release.

A missing credential scope returns HTTP 403 with code `credential_scope_required`, safe message `The authenticated credential does not grant this operation.`, and a correlation ID. Checklist item updates and normal Contact updates declare HTTP 409 because they carry concurrency state. The Contacts favorite command is an idempotent boolean write without an expected revision, so its contract deliberately does not advertise 409.

Note

The repository implementation and local battle environment expose and test this contract. Availability at api.buildwithhq.com still depends on deploying that gateway build, database grants, TLS, routing, and credentials to the production environment.

---

Capability review: 2026-09-14. [View the canonical guide](https://support.buildwithhq.com/api/headless-saas.html).
