Atribu
API Reference

Authentication

API key setup, scopes, rate limits, and security best practices

All API requests require a Bearer token in the Authorization header.

Authorization header
Authorization: Bearer atb_live_your_key_here

Getting a key

Create API keys from Settings > Developer in the Atribu dashboard. Each key is scoped to a single profile.

Show once

Keys are shown exactly once at creation. Store them securely — you cannot retrieve them later.

Scopes

Each API key has granular scopes that control what data it can access.

Analytics (read-only)

ScopeAccessEndpoints
analytics:readRead aggregated analytics: overview, timeseries, breakdowns, quality, touches, and channel/referrer/page/keyword/country/device/browser/OS dimensions.137
campaigns:readRead campaign, ad set and ad performance: ROAS, trends, archetypes, creative patterns, DM ads, forecast outlook and top performers.36
conversions:readRead conversion counts and revenue, including cash-only revenue and conversion timeseries.7
realtime:readRead the live visitor count.1

PII (read-only)

PII scopes

customers:read and visitors:read expose personal data (names, emails, journey details). Only grant these when your integration specifically needs customer-level data.

ScopeAccessEndpoints
customers:readRead the customer list and per-customer journey detail — personally identifiable information.19
visitors:readRead the visitor list — personally identifiable information.2

Commerce (read-only)

ScopeAccessEndpoints
commerce:readRead the merchant's product catalogue (products, variants, stock and prices) and look an order up by number, email or phone. Returns no customer identity.2

Conversion export (read, write)

ScopeAccessEndpoints
exports:readRead the conversion-export/signal-sync surface: Meta CAPI wiring diagnostics, dataset quality, Custom Audiences, export deliveries, and the compliance state behind Platform-Safe Mode.26
exports:writeConfigure the conversion-export pipeline: create, edit and disable export destinations (Meta dataset/pixel, Google Ads conversion action) and the signal rules that map a conversion definition to a platform event, and send a Meta test event.30

Provisioning (write)

ScopeAccessEndpoints
workspaces:writeCreate a workspace (POST /api/v1/workspaces). Not granted by any OAuth connect scope; an MCP user token holds it through the mcp:write grant.0
profiles:writeCreate a profile inside a workspace the caller already belongs to (POST /api/v1/profiles). Not granted by any OAuth connect scope; an MCP user token holds it through the mcp:write grant.0

Attribution & goals (write)

ScopeAccessEndpoints
events:writeIngest server-side outcome events (e.g. a closed sale) via POST /api/v1/events.3
goals:writeCreate, list and edit conversion goal definitions.14
attribution:writeTrigger an attribution recompute for a profile.41
tracking:writeConfigure a site's tracking installation: issue and revoke tracking keys, change the tracker's session/enrichment settings (which enqueues a full attribution replay), and start an install verification.11
reports:writeCreate, schedule, generate and send attribution reports (report schedules, deliveries and send).13
campaigns:applyApply or dismiss a campaign recommendation. Not granted by default — a workspace admin must opt a key into it.5
creatives:writeQueue an on-demand AI analysis of one ad's creative (POST /api/v1/ads/{id}/creative-analysis). Each accepted call queues a model call against the workspace's daily creative-analysis allowance, so it is capped per profile and per key per day and is not granted by default — a workspace admin must opt a key into it.5

Calendar (read, write, manage)

ScopeAccessEndpoints
calendar.readRead-only access to Atribu booking calendars and their events.5
calendarCreate, update and delete events on Atribu booking calendars.6
calendar.manageCreate, list and share (ACL) Atribu booking calendars.8

Messaging (write)

ScopeAccessEndpoints
whatsappSend and manage WhatsApp messages, templates, flows, broadcasts, calling and phone-number registration.47
instagramSend and manage Instagram DMs, comment replies, triggers and media.44
emailRead and send connected email (Gmail/Outlook): drafts, threads, labels and search.8

Messaging (read-only)

ScopeAccessEndpoints
whatsapp:readRead WhatsApp conversations, messages, templates and broadcasts. A read-only subset of whatsapp, which also allows sending and managing them.4

Inbox conversations (read, write)

PII scopes

conversations:read and conversations:write expose personal data — contact names, phone numbers and message content. Only grant these when your integration specifically needs inbox-level data.

ScopeAccessEndpoints
conversations:readRead the unified inbox: conversation list and message threads across WhatsApp, Instagram and email, plus organic/ad match suggestions, contact-extraction review proposals and the Instagram DM↔payment reconciliation worklist.8
conversations:writeReply on a conversation, save an email draft, apply Gmail labels, link/unlink a conversation to a customer, and approve/reject a contact-extraction review proposal.7

Default scopes when creating a key: analytics:read, campaigns:read, conversions:read, realtime:read.

The MCP user token — one credential for both surfaces

Since #1084 an MCP user token (atb_user_…) authenticates on /api/v1 exactly as it does on mcp.atribu.app. It is the credential for a person's own AI agent: one token, both surfaces, no separate API key to mint per profile.

Authorization header
Authorization: Bearer atb_user_your_token_here

How to get one

Through the MCP OAuth flow — the bootstrap for an agent with no Atribu account of its own. The agent registers a client (dynamic registration + PKCE) against https://mcp.atribu.app, builds the authorize URL and hands it to its human. The human signs in, approves the consent screen, and the agent holds the token. See MCP authentication.

Or mint one directly if you are already signed in: POST /api/v1/me/mcp-tokens, from Settings > Developer or from a session-authenticated call. The plaintext token is returned once and never again.

How it differs from an API key

An API key is a tenant — it is minted for exactly one profile. A user token names a person, who may reach several. So:

  • ?profile_id=<uuid> is required on profile-scoped routes. Its absence is a 400 invalid_request, never a guess. Workspace-scoped routes take ?workspace_id= (or the path segment) instead.
  • A profile the person cannot reach answers 404 not_found — byte-identical to a profile that does not exist, so the status line can never be used to enumerate tenants.
  • The workspace-grain routes an API key is refused on (/api/v1/workspaces, the cross-profile leaderboards) are open to a user token.

What the MCP scopes map to

A request's scopes are the intersection of two things: what the token's mcp:* grant maps to, and what the person's own workspace/profile membership already grants. Neither can widen the other.

MCP scopeAPI scopes it maps to
mcp:readanalytics:read, campaigns:read, conversions:read, realtime:read
mcp:read_piicustomers:read
mcp:writeexports:read, exports:write, goals:write, attribution:write, tracking:write, reports:write, workspaces:write, profiles:write, campaigns:apply

No MCP scope grants commerce:read, exports:read, exports:write, visitors:read, events:write, creatives:write, or any messaging, calendar or inbox scope. Those need an API key.

Because the answer is an intersection, a 403 insufficient_scope can mean either half is too narrow — the message names both, so you know whether to re-consent with a wider grant or ask a workspace admin for more access.

Routes a user token can never reach

Three groups of writes answer 403 insufficient_scope to a user token whatever its grant, because acting on them would let a credential administer the credential system itself. Widening the token's scopes does not change the answer; only a signed-in session (or, where it already worked, HTTP Basic) can do these.

RouteWhy
/api/v1/me/** — the whole subtree, including /me/mcp-tokensYour account: email, password, devices, sessions — and the tokens themselves. A token that could mint tokens could mint a wider one and revoke the one you would use to cut it off.
POST/PATCH/DELETE on /api/v1/workspaces/{id}/api-keys (and …/{id}/revoke, …/{id}/rotate)Minting an atb_live_ key. The key's scopes are chosen by the caller and are not bounded by the MCP grant, so a token that could mint one could mint itself out of every limit above. GET (the list, prefixes only) is a read and works.
PUT /api/v1/workspaces/{id}/mcp-settingspii_mode and mcp_writeback_enabled are the workspace's controls over what MCP tokens may do. An agent must not be able to widen the settings that govern agents. GET works — read them and tell your human what to change.

Account administration stays a session act

These are refusals by credential class, not by scope, so they are absent from each route's userToken security scheme in openapi.json too — the published spec says exactly what the runtime does. Sign in at www.atribu.app to manage your account, your API keys and your workspace's MCP settings.

Rate limits and revocation

A user token is metered against its own rate_limit_units_per_minute, in a bucket separate from both API keys and browser sessions — so an agent's loop cannot rate-limit its owner's dashboard, nor the reverse. A revoked or expired token answers 401 with the same envelope a revoked API key gets. Revoke one from Settings > Developer, or disconnect the OAuth client that holds it.

Rate limits

Default: 60 requests per minute per key.

Heavy endpoints (customers, visitors, journey detail) cost 3x — meaning they consume 3 units of your rate limit per call.

Endpoints with their own bucket

A few endpoints carry a sustained cap on top of the per-minute limit, because one call is far more work than a read. These buckets are separate from your per-minute allowance — they can neither consume it nor be consumed by it — and a 429 from one carries Retry-After.

EndpointOwn bucketWhy
POST /api/v1/quality/utm/refresh6 scans per hour, per keyEach call runs a live 8–12 second UTM-health scan. Within 10 minutes of the last scan the endpoint returns the cached summary with refreshed: false instead of scanning again.
POST /api/v1/profiles200 calls per hour, per app credentialEach call can create a user, a workspace and a profile — all durable.
POST /api/v1/ads/{id}/creative-analysis30 calls per hour, per key — and a daily budget: 20 analyses per profile and 100 per API key, both per UTC dayEach accepted call queues a multimodal model call on the ad's video, so this is the one endpoint that spends the workspace's daily AI allowance. The hourly bucket is the burst guard; the daily budget is the real ceiling, and a 429 from it carries Retry-After set to the seconds until the next UTC midnight. A repeat request for the same ad and the same unchanged creative inside 24 hours returns the existing job and consumes nothing. See Creative analysis.
HeaderDescription
X-RateLimit-LimitYour total limit per minute
X-RateLimit-RemainingRemaining requests in current window
X-Request-IdUnique request ID for debugging

When rate limited, you'll get a 429 response with a Retry-After header (seconds).

Key rotation

Rotate keys with zero downtime:

Call the Rotate endpoint — creates a new key

Both old and new keys work for 48 hours

Update your integration with the new key

The old key automatically expires after the grace period

Security best practices

Server-to-server only

This is a server-to-server API. Never expose keys in client-side JavaScript, mobile apps, or browser code.

  • Use the minimum scopes needed — don't grant customers:read if you only need analytics
  • Rotate keys regularly — the rotation endpoint makes this seamless
  • Revoke compromised keys immediately — revocation is instant

Error responses

Error response
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid or expired API key",
    "status": 401,
    "request_id": "req_a1b2c3d4"
  }
}
CodeStatusMeaning
unauthorized401Missing or invalid API key
insufficient_scope403Key doesn't have the required scope
rate_limit_exceeded429Too many requests
invalid_parameter400Bad query parameter
invalid_date_range400Date range exceeds 366 days or dates are invalid
internal_error500Server error — include the request_id when reporting

On this page