Permissions reference

Every AY Inbox API permission scope and what it allows, the presets such as Agent: read and draft, and the bitmask constants for building a scoped API key.

AY Inbox uses a bitmask system for API permissions. Each permission is one bit in a uint64, so a single integer can express any combination of the permissions below.

These same permissions are the OAuth scopes, lowercased: READ_EMAILS is the scope read_emails. An OAuth access token carries a bitmask of granted permissions and is checked through the identical gates as an API key.

Permission values

PermissionBitValueCategoryDescription
READ_EMAILS01readView email accounts and settings
READ_CAMPAIGNS12readView campaigns and sequences
READ_CONTACTS24readView contact lists, segments, notes, and activities
READ_UNIBOX38readAccess unified inbox
READ_ANALYTICS416readView analytics and statistics
WRITE_EMAILS532writeModify email account settings
WRITE_CAMPAIGNS664writeCreate and modify campaigns and sequences
WRITE_CONTACTS7128writeCreate and modify contacts, segments, notes, activities
WRITE_UNIBOX8256writeMark emails as read/unread and send replies
BULK_CONTACTS9512bulkBulk import/export/delete contacts
BULK_CAMPAIGNS101024bulkReserved for bulk campaign operations; no route checks it today
REALTIME_SUBSCRIBE112048specialSubscribe to realtime events
WEBHOOKS124096specialManage webhook endpoints
API_KEYS138192specialCreate and manage API keys
SEND_CAMPAIGNS1416384writeStart and stop campaigns, and approve, edit or reject first emails in a review queue (sends real mail)
READ_TEMPLATES1532768readView reply templates
WRITE_TEMPLATES1665536writeCreate and modify reply templates
READ_CRM17131072readView pipelines, deals, and CRM tasks
WRITE_CRM18262144writeCreate and modify pipelines, deals, CRM tasks
READ_AUDIT_LOGS19524288readView organization audit logs
INTEGRATIONS201048576specialConnect and manage third-party integrations
WARMUP_ROUTING212097152specialManage warmup routing rules
AI_AGENT224194304specialRun the AI assistant and MCP tools
AI_RESEARCH238388608specialRun AI contact research
READ_WORKSPACE2416777216readView the workspace profile, plan, limits, labels and credit balance
WRITE_WORKSPACE2533554432writeUpdate the workspace profile and what the AI is grounded on
AI_DRAFT2667108864specialHave the AI write reply drafts, charged to credits and never sent

READ_WORKSPACE covers GET /organization/current, its limits, the plan and trial at /subscription, and the label lists. The credit balance, usage, transactions and spend settings under /subscription/credits also need the member behind the credential to hold manage_billing. WRITE_WORKSPACE covers PATCH /organization/current, which also sets the product description, ideal customer notes and voice that every AI writing surface is grounded on, so it needs manage_settings and is never granted to a self-registered MCP client.

AI_DRAFT means "have the AI write a draft, charged to credits, never sent". It gates the generate_reply_draft tool on /v1/mcp and /ai/tools, and nothing else. It is separate from WRITE_UNIBOX, which also sends replies, and from AI_AGENT, which also writes playbooks, so a key that drafts is never a key that sends.

AI_AGENT gates the web and playbook tools (search_web, fetch_url, load_skill) on /v1/mcp and /ai/tools; every other tool there checks its own scope. It also gates the AI skills endpoints for keys; creating, editing or deleting a playbook with a key further needs the member who created the key to hold manage_settings.

SEND_CAMPAIGNS is intentionally separate from WRITE_CAMPAIGNS: editing a campaign draft and starting one that actually transmits mail are different blast radii, so a key can be granted the first without the second. An inbox placement test sends real mail from a real mailbox too, so starting or cancelling one, and scheduling a campaign's placement monitor, takes SEND_CAMPAIGNS, while reading results takes READ_ANALYTICS. A Looot lead job that adds its leads to a campaign which is already sending takes it too. Over the agent tools, linking a segment to a sending campaign, and widening a segment that already feeds one, take it as well. Contact writes do not: a contact that comes to match a segment linked to a sending campaign is enrolled, on REST and over the tools alike, so linking the segment is the decision that needs SEND_CAMPAIGNS. Approving a first email in a campaign's review queue lets that email go out, so approve, edit, reject and bulk-approve take SEND_CAMPAIGNS (for a member, send_campaigns), and so does turning review_first_emails off on a campaign that has it on, which releases every waiting email. Listing the queue takes READ_CAMPAIGNS. Turning on auto-reply, for the workspace or for a campaign, and changing what a campaign on automatic may send, take SEND_CAMPAIGNS (for a member, send_campaigns and a recent sign-in confirmation); WRITE_CAMPAIGNS can edit a campaign's auto-reply settings only while it is off or Supervised, and can lower an automatic campaign to Supervised or off. Every queue call also takes READ_CONTACTS (for a member, view_contacts), because each message shows its recipient's address, name and personalized text. Recording a contact's prior consent with PUT /contacts/:id/consent takes WRITE_CONTACTS, and the workspace's consent country list at /outreach/consent takes WRITE_CAMPAIGNS with manage_settings, like the other outreach settings.

Some scopes can spend a balance. Starting a Looot lead job, through its endpoint or the find_leads tool, takes INTEGRATIONS and BULK_CONTACTS and spends the instance's Looot balance. The find_leads tool also needs WRITE_CAMPAIGNS (and manage_campaigns for the member) when it names a campaign, the same as add_segment_to_campaign. A campaign that is sending (active, finished, or parked for undeliverable leads) mails what it gets, so naming one, through the endpoint or the tool, also needs SEND_CAMPAIGNS (and send_campaigns for the member), which a self-registered MCP client is never granted. The verify_email tool takes WRITE_CONTACTS, because a paid verifier charges for each check. Starting a first-line job with POST /contacts/first-lines takes AI_RESEARCH and BULK_CONTACTS, spends the instance's Looot balance and charges the workspace's AI credits. The generate_reply_draft tool takes READ_UNIBOX and AI_DRAFT and charges the workspace's AI credits, like the dashboard reply draft. A self-registered MCP client can be granted all of these except SEND_CAMPAIGNS, so approve them only for a client you trust to spend.

A new route or tool that spends takes a scope that is not read-only. Three published routes are older than that rule and keep their read scopes: POST /unibox/reply/draft and POST /unibox/compose/draft (READ_UNIBOX) and POST /emails/verify (READ_EMAILS).

Categories

Read

Read-only data access. Safe for monitoring, reporting, BI sync.

Write

Resource mutation, but no campaign starts. A key with the full write set can edit drafts but cannot turn on a campaign.

Bulk

High-volume operations. These can touch large numbers of rows in a single request, so they're broken out from the per-record write permissions.

Special

Realtime subscriptions, webhooks, integrations, warmup routing, AI assistant / MCP access, AI contact research, AI reply drafts, and self-service key management. Grant individually.

What a role can delegate

An API key or an OAuth grant acts for a member of the workspace, so it never holds a permission that member's role does not cover. Each API permission maps to the team role permission it needs; the workspace owner covers every one.

API permissionMember needs
READ_EMAILS, READ_CAMPAIGNS, READ_TEMPLATESview_campaigns
READ_CONTACTS, READ_CRMview_contacts
READ_UNIBOX, WRITE_UNIBOXaccess_unibox
READ_ANALYTICS, READ_AUDIT_LOGSview_analytics
WRITE_EMAILSmanage_emails
WRITE_CAMPAIGNS, WRITE_TEMPLATES, BULK_CAMPAIGNSmanage_campaigns
WRITE_CONTACTS, WRITE_CRM, BULK_CONTACTSmanage_contacts
SEND_CAMPAIGNSsend_campaigns
WEBHOOKS, WARMUP_ROUTINGmanage_settings
API_KEYSmanage_api_keys
INTEGRATIONSuse_integrations
AI_AGENTuse_ai
AI_RESEARCHview_contacts and use_ai
WRITE_WORKSPACEmanage_settings
AI_DRAFTaccess_unibox and use_ai
REALTIME_SUBSCRIBE, READ_WORKSPACEany member

Where it applies:

  • Creating or editing an API key from the dashboard refuses a permission outside the member's role with 403 api_key_permissions_exceed_caller.
  • Creating or editing an API key with an API key keeps the result inside the calling key: no permission it lacks, and when the calling key is limited to some mailboxes or IP addresses, the new or edited key must be limited to a subset of them. The same 403 api_key_permissions_exceed_caller answers otherwise.
  • An OAuth grant carries only the requested scopes the approving member's role covers, re-checked against the member's current role on every request. See OAuth scopes. API_KEYS is never granted to an OAuth app, and OAuth tokens cannot create, edit, revoke or delete API keys (403 oauth_token_not_allowed).
  • A member restricted to selected resources holds only view_campaigns, view_analytics and access_unibox from their roles, so a key or grant acting for them is limited to the read permissions those cover, and to the read routes and grants their scope allows (which routes). A key's own mailbox allowlist still applies on top. Restricted members cannot create keys.

Preset combinations

The three presets are returned by GET /api-keys/permissions under presets (read_only, agent_draft, full_access). The same response carries grantable, the mask the caller may put on a key (its role's reach, or the calling key's own permissions), and app_scopes, the mask an OAuth app may request. Existing keys keep exactly the bits they were created with: a key made before a permission existed does not gain it.

Read only (17465375)

All read permissions combined:

READ_EMAILS | READ_CAMPAIGNS | READ_CONTACTS | READ_UNIBOX | READ_ANALYTICS
  | READ_TEMPLATES | READ_CRM | READ_AUDIT_LOGS | READ_WORKSPACE
= 1 | 2 | 4 | 8 | 16 | 32768 | 131072 | 524288 | 16777216
= 17465375

Agent: read and draft (84574239)

Every read permission plus AI_DRAFT. This is the key to give a model that reads mail written by people outside the workspace: it can read and have the AI write drafts, charged to credits within the workspace's spend limits, and it holds no permission that writes or sends.

READ_ONLY | AI_DRAFT
= 17465375 | 67108864
= 84574239

A few published routes act on read permissions, and this preset reaches them: advisor refresh, snooze, dismiss and feedback on READ_ANALYTICS, and the reply and compose draft routes and POST /emails/verify, which spend credits.

Full access (134217727)

All 27 permissions:

(1 << 27) - 1 = 134217727

Agent sign-up key (17793791)

Not a preset you can pick: it is the key POST /agents/register mints for an AI agent that opened its own workspace. Every read permission plus the writes that set up a workspace, and nothing that sends mail, holds an AI scope (AI_DRAFT, AI_AGENT, AI_RESEARCH), or manages keys, webhooks or integrations.

READ_ONLY | WRITE_EMAILS | WRITE_CAMPAIGNS | WRITE_CONTACTS | BULK_CONTACTS
  | WRITE_TEMPLATES | WRITE_CRM
= 17465375 | 32 | 64 | 128 | 512 | 65536 | 262144
= 17793791

Some REST routes write with AI under the scopes it holds: like the read presets, the reply and compose draft routes, POST /templates/analyze under WRITE_TEMPLATES, and under WRITE_CAMPAIGNS the writing routes under /generation and a brain's sample-reply and organize. Until a person claims the workspace every one of them answers 403 agent_workspace_unclaimed, on any instance and whatever its billing, as does every route and tool that runs AI or a paid lookup. So do POST /emails/verify, adding or refreshing a web page as brain material, and publishing a form or setting a custom forms domain under WRITE_CONTACTS; a campaign tracking domain stays unverified, and contact import previews place columns without a model. After the claim they are charged to the workspace's AI credits and plan like anyone else's. When a person claims the workspace, with a code the agent redeems through POST /agents/claim (open to the agent's own key, no scope needed), every key the agent held ends.

Working with bitmasks

Combining

const READ_EMAILS = 1;
const READ_CAMPAIGNS = 2;
const SEND_CAMPAIGNS = 16384;

const permissions = READ_EMAILS | READ_CAMPAIGNS | SEND_CAMPAIGNS;
// 16387

Checking

function hasPermission(permissions, required) {
  return (permissions & required) === required;
}

hasPermission(16387, 16384); // true: SEND_CAMPAIGNS granted
hasPermission(16387, 64);    // false: WRITE_CAMPAIGNS missing

Rejecting unknown bits

POST /api-keys rejects any request whose permissions field has bits outside the known set, so a stale client can't accidentally request a future permission. The current mask of valid bits is 134217727.

Common permission sets

Monitoring integration

For dashboards and reporting:

const MONITORING =
  READ_EMAILS |     // 1
  READ_CAMPAIGNS |  // 2
  READ_ANALYTICS;   // 16
// 19

CRM sync

const CRM_SYNC =
  READ_CONTACTS |   // 4
  WRITE_CONTACTS |  // 128
  BULK_CONTACTS |   // 512
  READ_CRM |        // 131072
  WRITE_CRM;        // 262144
// 393860

Campaign automation (drafts only, no send)

const CAMPAIGN_DRAFTS =
  READ_CAMPAIGNS |    // 2
  WRITE_CAMPAIGNS |   // 64
  READ_CONTACTS |     // 4
  READ_TEMPLATES |    // 32768
  WRITE_TEMPLATES;    // 65536
// 98374

Campaign automation (with send)

Same as above, plus SEND_CAMPAIGNS (16384). Grant only when the integration genuinely needs to start campaigns.

Permission constants

export const Permissions = {
  READ_EMAILS: 1,
  READ_CAMPAIGNS: 2,
  READ_CONTACTS: 4,
  READ_UNIBOX: 8,
  READ_ANALYTICS: 16,
  WRITE_EMAILS: 32,
  WRITE_CAMPAIGNS: 64,
  WRITE_CONTACTS: 128,
  WRITE_UNIBOX: 256,
  BULK_CONTACTS: 512,
  BULK_CAMPAIGNS: 1024,
  REALTIME_SUBSCRIBE: 2048,
  WEBHOOKS: 4096,
  API_KEYS: 8192,
  SEND_CAMPAIGNS: 16384,
  READ_TEMPLATES: 32768,
  WRITE_TEMPLATES: 65536,
  READ_CRM: 131072,
  WRITE_CRM: 262144,
  READ_AUDIT_LOGS: 524288,
  INTEGRATIONS: 1048576,
  WARMUP_ROUTING: 2097152,
  AI_AGENT: 4194304,
  AI_RESEARCH: 8388608,
  READ_WORKSPACE: 16777216,
  WRITE_WORKSPACE: 33554432,
  AI_DRAFT: 67108864,
} as const;
class Permissions:
    READ_EMAILS = 1
    READ_CAMPAIGNS = 2
    READ_CONTACTS = 4
    READ_UNIBOX = 8
    READ_ANALYTICS = 16
    WRITE_EMAILS = 32
    WRITE_CAMPAIGNS = 64
    WRITE_CONTACTS = 128
    WRITE_UNIBOX = 256
    BULK_CONTACTS = 512
    BULK_CAMPAIGNS = 1024
    REALTIME_SUBSCRIBE = 2048
    WEBHOOKS = 4096
    API_KEYS = 8192
    SEND_CAMPAIGNS = 16384
    READ_TEMPLATES = 32768
    WRITE_TEMPLATES = 65536
    READ_CRM = 131072
    WRITE_CRM = 262144
    READ_AUDIT_LOGS = 524288
    INTEGRATIONS = 1048576
    WARMUP_ROUTING = 2097152
    AI_AGENT = 4194304
    AI_RESEARCH = 8388608
    READ_WORKSPACE = 16777216
    WRITE_WORKSPACE = 33554432
    AI_DRAFT = 67108864
const (
    APIPermReadEmails        uint64 = 1 << iota // 1
    APIPermReadCampaigns                        // 2
    APIPermReadContacts                         // 4
    APIPermReadUnibox                           // 8
    APIPermReadAnalytics                        // 16
    APIPermWriteEmails                          // 32
    APIPermWriteCampaigns                       // 64
    APIPermWriteContacts                        // 128
    APIPermWriteUnibox                          // 256
    APIPermBulkContacts                         // 512
    APIPermBulkCampaigns                        // 1024
    APIPermRealtimeSubscribe                    // 2048
    APIPermWebhooks                             // 4096
    APIPermAPIKeys                              // 8192
    APIPermSendCampaigns                        // 16384
    APIPermReadTemplates                        // 32768
    APIPermWriteTemplates                       // 65536
    APIPermReadCRM                              // 131072
    APIPermWriteCRM                             // 262144
    APIPermReadAuditLogs                        // 524288
    APIPermIntegrations                         // 1048576
    APIPermWarmupRouting                        // 2097152
    APIPermAIAgent                              // 4194304
    APIPermAIResearch                           // 8388608
    APIPermReadWorkspace                        // 16777216
    APIPermWriteWorkspace                       // 33554432
    APIPermAIDraft                              // 67108864
)

See also

On this page