Authentication Guide
How to authenticate with BuildStability's MCP server and API
Overview
Every request carries a bearer token, but there are two different credentials and they are not interchangeable. Using the wrong one is the usual reason an integration returns 401.
- OAuth client credentials — a client ID and secret you create yourself. This is the only thing the REST API
/api/v1/*accepts. Scoped, revocable on its own, counted against your plan's allowance, and refreshable without a human. Use this for integrations. - Your personal access token — the JWT behind your own login, from Copy Token. MCP (
POST /api/mcp) uses this. It expires after an hour, it can do anything your account can do including writes, and it cannot authenticate/api/v1/*. Treat it like a password.
Getting Your Access Token
Method 1: OAuth client credentials (for integrations)
This is what the REST API v1 requires. Nothing else authenticates it.
- Go to Business → Profile → API & Integrations and turn on Enable API/MCP Access
- Open API apps (it appears under Developers in the sidebar once access is on) and create an app
- Copy the
client_idandclient_secret. The secret is shown once and never again
Exchange them for an access token:
curl -X POST https://api.buildstability.com/api/oauth/token \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
# {"access_token":"...","token_type":"Bearer","expires_in":3600,"scope":"read write"}Then call the API with it:
curl https://api.buildstability.com/api/v1/clients \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Tokens last one hour. To refresh, make the same token call again — that is the programmatic refresh, and it needs no human. Scopes are read and write; omit scope to get everything the app was granted.
Method 2: Copy Token (personal token, for MCP)
When API access is enabled for your business:
- Go to Business → Profile → API & Integrations
- Turn on Enable API/MCP Access if needed
- Click Copy Token — your JWT is copied to the clipboard
Use it as Authorization: Bearer <token> for MCP. It does not work on /api/v1/*, which needs Method 1.
Method 3: Browser / Local Storage (quick test only)
- Log in to BuildStability at https://www.buildstability.com/login
- Open browser DevTools (F12) → Application → Local Storage
- Find
sb-<project-id>-auth-token - Copy the
access_tokenvalue
Method 4: In Code (Using Your Token)
Copy your token (Method 2), store it in an environment variable, and send it as a Bearer header:
const accessToken = process.env.BUILDSTABILITY_ACCESS_TOKEN;
const response = await fetch('https://api.buildstability.com/api/mcp', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' })
});Server-to-server sign-in
Use Method 1. OAuth client credentials are the server-to-server flow: no email, no password, no browser, and a fresh token whenever you ask for one. There is no separate programmatic sign-in to apply for.
There is no /api/auth/login endpoint. Backend auth routes (/api/auth/native-*, /api/auth/web-*, /api/auth/magic-link-*) are gated by origin, user-agent, and Turnstile for first-party apps only.
What a JWT Looks Like
A JWT has three base64url-encoded segments separated by dots: header.payload.signature. The header describes the algorithm; the payload holds claims (e.g. sub, email, exp); the signature ensures integrity. Tokens are usually 200–500+ characters.
Example format (always use your own from Copy Token—tokens are user-specific and expire in 1 hour):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJZT1VSLVVTRVItSUQiLCJlbWFpbCI6InlvdUBleGFtcGxlLmNvbSIsInJvbGUiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjo5OTk5OTk5OTk5fQ.EXAMPLE_SIGNATURE_NOT_A_REAL_TOKEN
What to do with it: Put it in the Authorization: Bearer <your-token> header for MCP (POST /api/mcp), the REST Data API, or any BuildStability API that uses JWT auth.
Using the Token
MCP Requests
curl -X POST https://api.buildstability.com/api/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{ ... }'REST Data API Requests
The REST Data API is generated from the database schema and takes the same personal token. Row Level Security filters every query to the businesses you belong to, and no database credentials are exposed.
It is not read-only, and it is not metered. The personal token can insert, update and delete wherever your account can, these calls do not pass through our API so they are not counted against the allowance shown on your Business page, and the token is your login rather than a scoped key. It is a good tool for your own queries and a poor one to hand to anybody else. For that, use OAuth client credentials and the REST API v1.
Detailed technical documentation (hosts, endpoints, schemas) is available to registered developers. Enable API access in Business → Profile → API & Integrations.
See REST Data API Guide for detailed usage.
Token Expiration
Access tokens expire after 1 hour. Refresh tokens are valid for 30 days.
Refreshing a Token
For a personal token, click Copy Token again in Business → Profile → API & Integrations. For a server-to-server integration there is nothing to click: repeat the/api/oauth/token call from Method 1 with the same client ID and secret. That is the whole refresh story, which is why an integration should never be built on a personal token.
Automatic Refresh (Logged-In Session)
While you are logged in to the BuildStability web app, your session token refreshes automatically in the background. The token in local storage (Method 2) is always the current one.
Security Best Practices
1. Never Commit Tokens
# .gitignore .env *.token secrets.json
2. Use Environment Variables
const accessToken = process.env.BUILDSTABILITY_ACCESS_TOKEN;
3. Store Tokens Securely
- Server-side: Use environment variables or a dedicated secrets manager
- Client-side: Use secure storage (not localStorage for sensitive apps)
- Mobile: Use Keychain (iOS) or Keystore (Android)
4. Rotate Tokens Regularly
If a token is compromised, log out of the session it came from. Logging out revokes the refresh token, and the access token itself expires within an hour.
5. Use HTTPS Only
Always use https:// endpoints. Never send tokens over HTTP.
Multi-Tenancy & API Access
BuildStability uses business-level isolation with explicit API opt-in:
Security Model
- API Access Must Be Enabled: Your business administrator must enable API access before MCP endpoints work.
- Business Isolation: You can only access data for your business. All queries are automatically filtered by
business_id. - Multi-Business Support: If you belong to multiple businesses, use the
X-Business-IDheader to specify which business to access.
Enabling API Access
Business administrators can enable API access:
- Go to Business → Profile → API & Integrations
- Toggle Enable API Access and save if needed
- Click Copy Token to copy your JWT to the clipboard
Multi-Business Users
If you belong to multiple businesses with API access enabled:
# Specify which business to access
curl -X POST https://api.buildstability.com/api/mcp \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Business-ID: your-business-uuid" \
-H "Content-Type: application/json" \
-d '{ ... }'Without the X-Business-ID header, the system uses your first API-enabled business.
API Keys vs Access Tokens
- OAuth client ID + secret: Per-app, created by you, revocable on its own. The only credential the REST API v1 accepts. Use this for anything you are handing to another company.
- Personal access token (JWT): Your own login. Expires after 1 hour, carries read and write, cannot be revoked without signing out. Use for MCP and your own scripts.
- Anon Key: Public by design, never expires. Grants limited unauthenticated access to the REST Data API (details for registered developers).
For MCP, use the personal access token. For an integration, use OAuth client credentials.
Troubleshooting
"Business context required" Error
- Your token is missing or invalid
- Token has expired (refresh it)
- You're not authenticated
- API access not enabled for your business
"Invalid Request" Error
- Check your JSON-RPC format
- Verify
jsonrpc: "2.0"is present - Ensure
methodandidare included
"Method not found" Error
- Check the method name spelling
- Verify you're using the correct endpoint (
/api/mcp) - Review available methods with
tools/list