Authentication
Two credential types, one authorization model.
| Credential | Prefix | Issued by | For |
|---|---|---|---|
| API key | mxa_live_ |
App → Account → API | Servers, scripts, automation platforms |
| OAuth access token | mxa_oat_ |
auth.mxaura.ai (OAuth 2.1) |
MCP clients and browser apps acting for a signed-in user |
Both are sent the same way:
Authorization: Bearer <credential>
(x-api-key: <credential> is also accepted for API keys.)
Both are graded by the same scopes, the same rate limits, and the same tenant boundary: a credential sees exactly one account's data. That boundary is enforced in the database access layer for every statement, not by the API code checking ids.
API keys
Lifecycle
| Action | Where | Notes |
|---|---|---|
| Create | Account → API → Create key | Shown once. Choose show-once (default) or retrievable (can be revealed again later; audited). |
| Rotate | Account → API → Rotate | Issues a new key; the old one keeps working for a grace period and answers with X-MXA-Key-Status: rotated_grace so you can find lingering callers. |
| Revoke | Account → API → Revoke | Immediate. The edge cache is flushed the moment you click. |
| Expire | Set at creation | A key past expires_at answers 401 key_expired. |
| Suspend | Platform | A suspended key answers 401 with the reason. |
Keys are stored as a SHA-256 hash. mxAURA cannot show you a show-once key again; rotate it.
Per-key settings (Account → API → Settings)
- Which addresses may use this key — an optional allowlist of IPv4/IPv6
addresses or CIDRs. Empty means any address, which is the default. With
entries, a request from anywhere else gets
403 ip_not_allowed. - How many requests per minute — 60, 120 (default), 300 or 600. See rate limits.
Keys and browsers
API keys are refused from browsers: a request carrying an Origin header
gets 403 browser_origin. A key in JavaScript is a key in every visitor's
hands. Browser apps use OAuth tokens instead.
OAuth 2.1 (MCP clients and browser apps)
https://auth.mxaura.ai is a standards-compliant OAuth 2.1 authorization
server:
| Endpoint | Purpose |
|---|---|
/.well-known/oauth-authorization-server |
Discovery (RFC 8414) |
/register |
Dynamic Client Registration (RFC 7591) — any client may register itself |
/authorize |
Authorization code with PKCE S256 mandatory (plain rejected) |
/token |
authorization_code and refresh_token grants |
Consent rides the user's mxAURA session: they sign in to the app (with 2FA if enabled), see the requesting client and the scopes, and approve. The token that results is bound to the account they were signed into.
- Access tokens live 1 hour; refresh tokens 30 days.
- Refresh tokens rotate on use; a used refresh token cannot be replayed. To cut off a client, remove the connection in the client itself; its access token expires within the hour and it cannot refresh. (An in-app "connected apps" page for revoking from mxAURA's side is on the roadmap.)
- Tokens are graded by the public API like keys — same scopes, limits, caps.
Most MCP clients handle all of this for you: give them
https://mcp.mxaura.ai/mcp and they discover the rest. See the
MCP guide.
Scopes
Scopes are family.verb. read covers GET; write covers POST, PUT, PATCH,
DELETE. full grants everything the account may do over the API.
| Family | Covers |
|---|---|
components |
messages, headers, summaries, signatures, senders, templates |
brandings |
brandings |
pools |
pools and their composed messages |
campaigns |
campaigns, campaign pools, campaign simulation |
flows |
flows, flow blueprints |
enrollments (write only) |
enrolling / unenrolling contacts in flows — this sends real email |
dispatch (read only) |
the dispatch log and activity feed |
hubs |
hubs, layouts, stats, the hub's own settings |
articles |
articles, authors, content types |
publishing |
placing, publishing, scheduling, featuring articles; opening a preview window |
subscribers |
subscriptions (embed keys), site keys, assignments — who may embed |
identity |
the messaging identity (frameworks, sources, advisor) |
folders |
folders |
media |
the account's own media library |
webhooks |
outbound event endpoints |
ai (write only) |
anything that calls the model and spends budget: hero images, SEO, drafts, advisor generation |
The hubs family is split on purpose: a key for a content team gets
articles.write + publishing.write without the ability to mint embed keys
(subscribers.write) or spend model budget (ai.write).
The support profile
Every read family, nothing that sends, publishes or spends:
components.read brandings.read pools.read campaigns.read flows.read
hubs.read articles.read identity.read dispatch.read
It is the default grant for an OAuth client that does not ask for
specific scopes, and a client can request it by name with scope=support.
It is what a help agent should hold: enough to answer "why didn't Darren get
the email" — never enough to send one.
Not available over the API — on purpose
Accounts, users, profiles, billing, HubSpot connections/OAuth, notifications,
import/export and platform administration are session-only surfaces in the
app. Two destructive actions are also withheld even with write scope:
deleting a hub (it cascades into every client site embedding it) and deleting
the messaging identity (everything composes from it). Rotating a client's
site key is likewise app-only: it revokes every live embed at once.