Workspaces & Profiles
Create the container and the account being measured — the first two objects that have to exist before anything else works.
A workspace is the top-level container: billing, members, branding, API keys. A profile is one advertised business inside it — one set of ad accounts, one CRM, one tracker, one attribution answer. Every profile-scoped route on this API answers for one profile.
Most businesses need one workspace and one profile. An agency needs one workspace and a profile per client.
Creating them without a browser
POST /api/v1/workspacesTwo unrelated ways to call it, sharing one URL — the same shape
POST /api/v1/profiles uses.
- Session bearer — a Supabase session token or an
atb_user_…MCP token: you create your own workspace and become its owner. Body:{"name": "Acme", "timezone": "America/Santiago", "plan": "growth"}. - HTTP Basic — a registered consumer app creating a bare workspace for one
of its users, identified by
email. No profile and no delegation grant are created; follow with your ownPOST /api/v1/profiles.
plan defaults to starter (Free). growth / agency immediately write a
trialing subscription at that plan's real limits rather than waiting for the
first profile to bootstrap Free — so an agent that creates the workspace before
its first POST /profiles does not silently sit on Free believing it asked for
Growth. enterprise is sales-led and is not accepted here.
Safe to retry — the session branch is idempotent on (caller, name)
A retried call with the same name inside a short window returns the same
workspace with created: false and 200, so a dropped connection never leaves
two workspaces behind. Minutes later, the same name is a genuinely new
workspace — a name is legitimately reusable. The app-credential branch has no
idempotency at all: every call creates, and created is always true.
checkout — the plan you asked for, paid for in the same round trip
A growth or agency create returns a checkout hand-off alongside the new
workspace, so an agent that knows which plan it wants never mints one
separately:
{
"data": {
"workspace_id": "…",
"name": "Acme",
"created": true,
"checkout": {
"id": "0f2f6b0a-9a2c-4f4e-9a1e-6f7f2a5c3b21",
"url": "https://www.atribu.app/h/9tQ2mB1x…",
"expires_at": "2026-09-05T11:30:00.000Z"
}
}
}Hand checkout.url to the person who pays and poll
GET /api/v1/handoffs/{id} — it is an ordinary
checkout hand-off, the same object
POST /api/v1/workspaces/{workspaceId}/checkout-session mints. See
Billing.
Workspace creation is never blocked on billing
checkout is null for a starter request, and also null when the mint
itself failed — unconfigured billing, or Stripe refusing — in which case the
response still answers 201 and carries
warnings: [{"code": "checkout_unavailable", "message": "…"}]. The workspace
exists and created is still true: a payment link that could not be produced
must not cost you the workspace you just made. Read warnings, then retry the
mint alone with POST /api/v1/workspaces/{workspaceId}/checkout-session.
Two shapes to parse correctly: warnings is omitted, not an empty array,
when nothing degraded — and an idempotent replay never mints a second
checkout, so the created: false response carries checkout: null even for a
growth request. Capture checkout from the call that actually created the
workspace. checkout is always null on the app-credential branch.
POST /api/v1/profilesPOST /api/v1/profiles has two branches. The app-credential branch
provisions a dealer end to end (see Partners & OAuth apps);
the session branch is what a person or their agent uses for their own next
client. A profile creation is refused with plan_limit_reached once the
workspace's active_profiles allowance is spent — that limit is enforced on
every creation path, so an agent that plans to create ten profiles should read
the subscription first.
Retiring a workspace
DELETE /api/v1/workspaces/{workspaceId}Owner only, and terminal — there is no un-archive. A workspace created by mistake, or a throwaway one an agent made while exploring, is retired with this call.
"Delete" means ARCHIVE, and the response says so. A workspace that has ever
exported holds conversion_exports rows, and a database trigger refuses to let
any path erase them — they are the record of what Atribu transmitted on your
customers' behalf. So the call stamps archived_at, removes every membership,
revokes pending invitations, archives every profile and purges its tracking,
attribution and spend data, revokes the workspace's API keys and pauses its
webhook subscriptions. What survived is in counts.retained_*.
{
"data": {
"workspace_id": "873efefe-5fe0-44d5-8f67-0d3967855ff6",
"archived": true,
"archived_at": "2026-09-06T14:42:11.547Z",
"counts": {
"profiles_archived": 1,
"memberships_removed": 1,
"invitations_revoked": 0,
"api_keys_revoked": 0,
"webhook_subscriptions_paused": 0,
"retained_conversion_exports": 1,
"retained_signal_audit_rows": 3
},
"profiles_purged": 1
},
"meta": { "workspace_id": "873efefe-5fe0-44d5-8f67-0d3967855ff6" }
}Safe to retry — idempotent, and both answers are 200
The first call returns archived: true. Every later call returns
archived: false with the original archived_at and changes nothing. A
DELETE that 404s an already-archived workspace would make a retry look like a
failure.
A session bearer, or an atb_user_… token whose grant covers
workspaces:write — a default mcp:read token is refused. An atb_live_ API
key cannot call it: a key is minted for exactly one profile and belongs to no
workspace. A member who is not an owner gets 403; a non-member gets
404, the same answer an unknown id gets, so the status code never confirms
that a workspace exists.
After archiving, every workspace-scoped route answers 404 for it, it is absent
from GET /api/v1/workspaces and from MCP whoami, and a pending invitation to
it can no longer be accepted.
Reading what you can reach
GET /api/v1/workspaces is the right first call for a user credential: it
needs no profile_id, and it tells you which workspaces the person behind the
token belongs to. GET /api/v1/workspaces/{workspaceId}/profiles then lists
the profiles inside one.
A user token must name a profile
An API key is one profile, so profile-scoped routes need no parameter. An
atb_user_… token may reach several, so ?profile_id=<uuid> is required
there — its absence is a 400, never a guess. See
Authentication.
Everything under /workspaces
The workspace is also the grain for cross-profile analytics (Top Performers,
creative patterns, experiments, pacing), for agency reporting, and for the
admin surfaces a console needs. Those routes live under /workspaces/{id}/…
because they are workspace-scoped, and they are explained where the feature is:
Top Performers and creative reads,
reports, credentials.
| Method | Path | What it does |
|---|---|---|
POST | /api/v1/invitations/accept | Accept a workspace or profile invitation |
GET | /api/v1/profiles | List the profiles the signed-in user can reach in this workspace |
POST | /api/v1/profiles | Provision a dealer (app-authenticated) |
DELETE | /api/v1/profiles/{profileId} | Archive a profile you provisioned (app-authenticated) |
POST | /api/v1/profiles/{profileId}/adopt | Adopt an existing profile (app-authenticated) |
PUT | /api/v1/profiles/{profileId}/entitlements/{feature} | Set a profile's Atribu entitlement (app-authenticated) |
POST | /api/v1/profiles/demo | Create and seed this workspace's demo profile |
DELETE | /api/v1/profiles/demo | Delete this workspace's demo profile |
GET | /api/v1/workspaces | List the workspaces the signed-in user belongs to |
POST | /api/v1/workspaces | Create a workspace |
DELETE | /api/v1/workspaces/{workspaceId} | Archive a workspace |
GET | /api/v1/workspaces/{workspaceId}/api-keys | A profile's API keys |
POST | /api/v1/workspaces/{workspaceId}/api-keys | Mint a new API key for a profile |
POST | /api/v1/workspaces/{workspaceId}/api-keys/{id}/revoke | Revoke one API key |
POST | /api/v1/workspaces/{workspaceId}/api-keys/{id}/rotate | Rotate one API key |
GET | /api/v1/workspaces/{workspaceId}/api-usage | Per-key API usage for a workspace |
GET | /api/v1/workspaces/{workspaceId}/audit | List recorded changes to a workspace's classification settings |
POST | /api/v1/workspaces/{workspaceId}/billing-portal-session | Open Stripe's Customer Portal for a workspace |
GET | /api/v1/workspaces/{workspaceId}/branding | The workspace's report/email branding |
PUT | /api/v1/workspaces/{workspaceId}/branding | Update the workspace's branding (non-logo fields) |
POST | /api/v1/workspaces/{workspaceId}/branding/logo | Upload the workspace's logo |
DELETE | /api/v1/workspaces/{workspaceId}/branding/logo | Remove the workspace's logo |
POST | /api/v1/workspaces/{workspaceId}/checkout-session | Mint a Stripe Checkout hand-off for a plan upgrade |
GET | /api/v1/workspaces/{workspaceId}/compliance-overview | Per-profile privacy/HIPAA compliance posture |
PATCH | /api/v1/workspaces/{workspaceId}/compliance-overview | Set whether this workspace is a healthcare agency |
GET | /api/v1/workspaces/{workspaceId}/connected-apps | List OAuth-app authorizations across the workspace's profiles |
DELETE | /api/v1/workspaces/{workspaceId}/connected-apps/{id} | Revoke an OAuth-app authorization |
GET | /api/v1/workspaces/{workspaceId}/creative-archetypes | The workspace's creative archetypes and how often each one wins |
GET | /api/v1/workspaces/{workspaceId}/creative-patterns | Cross-profile creative patterns, and who is not running them |
GET | /api/v1/workspaces/{workspaceId}/creative-patterns/dimensions | Which single creative-dimension values win in this workspace |
POST | /api/v1/workspaces/{workspaceId}/creative-tests/decisions | Dismiss or draft one testing-roadmap gap |
POST | /api/v1/workspaces/{workspaceId}/customer-erasures | Erase a customer's personal data |
POST | /api/v1/workspaces/{workspaceId}/experiment-promotions | Record an ad's promotion to a Meta experiment |
GET | /api/v1/workspaces/{workspaceId}/experiments | Meta lift and split studies across the workspace's profiles |
GET | /api/v1/workspaces/{workspaceId}/invitations | List pending workspace invitations |
POST | /api/v1/workspaces/{workspaceId}/invitations | Invite someone to the workspace |
DELETE | /api/v1/workspaces/{workspaceId}/invitations/{invitationId} | Revoke a pending workspace invitation |
GET | /api/v1/workspaces/{workspaceId}/mcp-settings | Get the workspace's MCP data-plane settings |
PUT | /api/v1/workspaces/{workspaceId}/mcp-settings | Update the workspace's MCP data-plane settings |
GET | /api/v1/workspaces/{workspaceId}/mcp-writeback-audits | List the workspace's MCP write-back audit log |
GET | /api/v1/workspaces/{workspaceId}/members | List a provisioned workspace's members (app-authenticated) |
PUT | /api/v1/workspaces/{workspaceId}/members | Add or update a workspace member (app-authenticated) |
DELETE | /api/v1/workspaces/{workspaceId}/members/{userId} | Remove a workspace member (app-authenticated) |
GET | /api/v1/workspaces/{workspaceId}/oauth-apps | OAuth apps this workspace administers, with their DPA state |
POST | /api/v1/workspaces/{workspaceId}/oauth-apps/{appId}/dpa/accept | Accept the DPA for every profile an OAuth app provisions |
POST | /api/v1/workspaces/{workspaceId}/oauth-apps/{appId}/dpa/withdraw | Withdraw an OAuth app's DPA acceptance |
GET | /api/v1/workspaces/{workspaceId}/pacing | Month-to-date spend and outcomes against budget, per profile |
GET | /api/v1/workspaces/{workspaceId}/pii-access-log | Audit access to this workspace's customer/visitor PII |
GET | /api/v1/workspaces/{workspaceId}/profiles | List the profiles the signed-in user can open in a workspace |
GET | /api/v1/workspaces/{workspaceId}/profiles/{profileId}/invitations | Pending invitations to one profile |
POST | /api/v1/workspaces/{workspaceId}/profiles/{profileId}/invitations | Invite someone to one profile |
DELETE | /api/v1/workspaces/{workspaceId}/profiles/{profileId}/invitations/{invitationId} | Revoke a pending profile invitation |
PATCH | /api/v1/workspaces/{workspaceId}/profiles/{profileId}/members/{membershipId} | Change a profile member's role or permissions |
DELETE | /api/v1/workspaces/{workspaceId}/profiles/{profileId}/members/{membershipId} | Remove someone from a profile |
GET | /api/v1/workspaces/{workspaceId}/recommendations | Open recommendations across every profile in the workspace |
GET | /api/v1/workspaces/{workspaceId}/replicate-runs/{runId} | One Replicate run — a winning ad adapted for another profile |
POST | /api/v1/workspaces/{workspaceId}/replicate-runs/{runId}/handoff | Bind a finished Replicate run to the creative session it was handed to |
GET | /api/v1/workspaces/{workspaceId}/reporting-currency | The currency this workspace's cross-profile totals are reported in |
PATCH | /api/v1/workspaces/{workspaceId}/reporting-currency | Set the currency this workspace's cross-profile totals are reported in |
POST | /api/v1/workspaces/{workspaceId}/reports/bulk-generate | Generate (and optionally send) a report across many profiles |
GET | /api/v1/workspaces/{workspaceId}/reports/hub | The workspace Reports hub |
POST | /api/v1/workspaces/{workspaceId}/reports/schedules/bulk | Apply a schedule patch across many profiles |
GET | /api/v1/workspaces/{workspaceId}/sender-domain | The workspace's verified sending domain, if any |
POST | /api/v1/workspaces/{workspaceId}/sender-domain | Register a sending domain for this workspace |
DELETE | /api/v1/workspaces/{workspaceId}/sender-domain | Remove the workspace's sending domain |
POST | /api/v1/workspaces/{workspaceId}/sender-domain/verify | Re-check DNS verification for the workspace's sending domain |
POST | /api/v1/workspaces/{workspaceId}/shopify-plan-selection | Choose a plan on a Shopify App Store workspace |
GET | /api/v1/workspaces/{workspaceId}/snapshots | List shared Top Performers snapshot links |
POST | /api/v1/workspaces/{workspaceId}/snapshots | Capture a shareable Top Performers snapshot |
DELETE | /api/v1/workspaces/{workspaceId}/snapshots/{snapshotId} | Revoke a shared snapshot link early |
GET | /api/v1/workspaces/{workspaceId}/subscription | The workspace's current subscription — plan, status, period bounds |
GET | /api/v1/workspaces/{workspaceId}/tab-counts | How much is new on each console tab since you last opened it |
GET | /api/v1/workspaces/{workspaceId}/test-roadmap | What each profile should test next, and how much creative it has to judge on |
GET | /api/v1/workspaces/{workspaceId}/top-performers | The workspace's top-performing ads, across every profile |
GET | /api/v1/workspaces/{workspaceId}/top-performers/brief | The six counters that summarise a workspace's leaderboard |
POST | /api/v1/workspaces/{workspaceId}/top-performers/chat | Ask the Top Performers Copilot (streaming) |
Generated from openapi.json. The full request and response schema for every operation is in the OpenAPI document.