Menu

Authentication and API keys

API keys for your systems, OAuth 2.1 with PKCE for apps that act on behalf of a person, and how to target an Account.

Every request carries Authorization: Bearer <credential>. There are two kinds of credential and both act as a person of the organization: they can never do more than that person.

CredentialPrefixWhat for
API keymbs_live_…Your servers, scripts, Make, Zapier or Cursor. An owner or admin creates it and it represents a member.
OAuth tokenmbo_… (and mbr_… to refresh it)An app that acts on behalf of the person who authorizes it: Claude, ChatGPT or your integration.

API keys

  • Where: Settings → API & MCP → API keys (owners and admins). The API key is shown only once; the list keeps its prefix so you can recognize it.
  • Who it represents: a member of the organization (you, by default). Nobody can create an API key on behalf of someone with a higher role than theirs.
  • Permissions: per module, Read or Read and write; Allow deletions is separate and off by default. See Permissions and scopes.
  • Expiry: never, or 30, 90, 365 days.
  • Rotate creates a new API key with the same permissions; the previous one keeps working for 24 hours so you can switch the integration without a gap.
  • Suspend, reactivate and revoke from the same tab. If the person it represents leaves the organization, the API key is suspended automatically.
  • Limit: the number of active API keys depends on the plan (see Limits).
The API is server to server: it does not answer requests from a browser (CORS is closed). An API key in a web page's code is exposed; if that happens, rotate or revoke it right away.

Test API keys: the practice organization

A test API key (mbs_test_ and 46 characters) works only on your workspace's practice organization: a separate organization with example CRM data (a pipeline with its stages, companies, persons, deals in different stages and activities) and a few activity labels to try labeling entries (a practice organization created before 2026-10-12 gets them when reset with platform.sandbox.reset), created on its own with the first test API key. Use it to build and try an integration without touching real data.

  • How to create one: in Settings → API & MCP → API keys, Create API key with the Test environment (workspaces only). It acts as an owner or admin of the workspace and does not reach Accounts: MB-Organization and org_id change nothing, the request always goes to the practice organization.
  • Nothing leaves MB Suite: sending an email (crm.email.send) or a WhatsApp message (crm.whatsapp.send_text, send_template, send_media) is simulated: the copy stays in the practice organization (the email's activity, the message in its conversation) and the response carries simulated: true (in the API, also the MB-Simulated: true header). Exporting a PDF is simulated too: nothing is rendered and a one-page PDF that says so is stored (dashboards.dashboards.export_pdf returns the link; reports.instances.export_pdf answers queued and the link comes from reports.instances.pdf_url). Anything else that reaches another platform answers 409 sandbox_external_unavailable. GET /v1/me and mb_context list in practice what is simulated.
  • Its own quota: the practice organization has its own limits, the Practice row of Limits, and never uses your workspace's quota.
  • Test webhooks: in the Webhooks tab, the Test environment creates subscriptions in the practice organization. They receive only what test API keys do and are delivered for real, signed the same way; no live subscription receives events from the practice organization.
  • Reset test data: from the Practice organization card of the API keys tab, or with POST /v1/sandbox/reset (first with dry_run). It deletes everything the practice organization accumulated and seeds the example data again; API keys and test subscriptions are kept. GET /v1/sandbox returns its state.
curl https://api.mb-suite.com/v1/crm/deals?limit=5 \
  -H "Authorization: Bearer $MB_TEST_KEY"

Workspaces and Accounts: MB-Organization

A workspace API key can work on its Accounts if you chose Also access Accounts when you created it (all or some, and always only the ones the person can see). To point a request at an Account, send its id in the MB-Organization header (or org_id in the query or the body). Without it, the request goes to the API key's organization.

curl https://api.mb-suite.com/v1/crm/deals \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "MB-Organization: ACCOUNT_ID"

An organization the API key cannot reach answers 404 organization_not_found: we never say whether it exists. An Account can also have its own API keys if it pays its own subscription or if its agency allows it (Partners → View permissions).

OAuth 2.1 for apps

An app that acts on behalf of a person uses the authorization_code flow with PKCE (S256, required). Clients are public, without a secret. Everything you need is in the discovery document: https://mb-suite.com/.well-known/oauth-authorization-server.

  • Registration: dynamic (RFC 7591) at https://mb-suite.com/api/platform/oauth/register, with https:// or loopback-with-port redirect_uris; or a client_id that is the URL of your metadata document.
  • Authorize: send the person to https://mb-suite.com/oauth/authorize with resource (required, RFC 8707): https://api.mb-suite.com for the API or https://mcp.mb-suite.com for the MCP. A token only works for the resource you requested.
  • Consent: the person picks the organization and reviews the permissions (writing and deleting are off), the Accounts and, if the organization allows it, connected mailboxes and calendars.
  • Tokens: the access token (mbo_…) lasts 1 hour; the refresh token (mbr_…), 30 days, and it is single use: every refresh returns a new one and reusing an old one revokes the whole authorization. No authorization lasts longer than 365 days.
  • Revoke: https://mb-suite.com/api/platform/oauth/revoke (RFC 7009), or the person from Settings → API & MCP → MCP, with “Disconnect”. It cuts access instantly.
GET https://mb-suite.com/oauth/authorize
  ?response_type=code
  &client_id=CLIENT_ID
  &redirect_uri=https://app.example.com/callback
  &code_challenge=CODE_CHALLENGE
  &code_challenge_method=S256
  &resource=https%3A%2F%2Fapi.mb-suite.com
  &scope=crm:write
  &state=RANDOM_STATE

Scopes

scope uses the same permissions as API keys: <module>:read, <module>:write and <module>:delete (the deletion switch), plus connected_content:read for connected mailboxes and calendars. Without scope, the app asks only for read access to the modules with operations. Writing is always the person's call in the consent screen, even when the app requests it with scope: “Read and write” is a switch that is off by default (with scope, it only covers the modules where the app asked for :write), and Members, Branding, Accounts, Client Portal, Email Marketing and Integrations each have their own. Deleting is separate, and developer_platform (creating webhooks) must be requested explicitly. Ask only for what your integration uses: whatever the person leaves off is not in the token, and the scope in the /token response lists what was granted.

The OAuth endpoints answer with standard OAuth errors (invalid_request, invalid_grant, invalid_client…), not with the API's error format.