Authentication

Org API keys, scopes, acting on a child org, and how PII is masked

Any Wexio organization on a qualifying plan can mint an Org API key and drive its own inbox from a backend. There is no separate partner credential - a partner is simply an org whose key may also act on the client orgs it manages.

The API needs Standard or higher, and MCP needs Pro.

SurfacePlan
GraphQL API with a keyStandard, Pro or Enterprise
MCP serverPro or Enterprise
WebhooksPro or Enterprise

This is checked on every call, not only when the key is minted. Downgrade and existing keys stop working; upgrade and they work again. Keys are never revoked for a plan change, so you do not have to re-issue them - see what a downgrade does.

Tech providers and their client organisations are not affected: they are billed by usage rather than a retail plan.

Request Headers

Authorization: Bearer wx_9f3c1a2b4d5e6f70_kQ8sN1pT4vR7wZ2yB5eH8jL0mQ3sV6x
HeaderRequiredValue
AuthorizationYesBearer followed by the key, wx_<keyId>_<secret>.
X-Wexio-OrgNoA managed child org to act on instead of your own. Only a tech provider's key may use it.

A key always acts on its own org unless X-Wexio-Org names a child the caller actually manages.

The raw X-Wexio-Org header is never trusted on its own. The server checks that the key's org is a TECH_PROVIDER and that the named org is one of its managed children. If it is not, the request fails - a key cannot reach sideways into an unrelated org by guessing an ID.

Scopes

Every key carries an explicit list of scopes, chosen at mint time. A call is rejected unless the key holds the scope the operation requires.

ScopeUnlocks
MESSAGES_READReading conversations, messages, search, media and media counts
MESSAGES_SENDSending, editing and deleting messages; reactions; media upload
CONVERSATIONS_MANAGEInbox state: read/unread, close/reopen, assign, block, labels, collections, AI toggle, custom fields
CONTACTS_READReading and searching contacts; reading people-field definitions and values
CONTACTS_MANAGECreating and updating contacts; managing field definitions and values
CONTACTS_ERASEPermanently erasing a contact (GDPR right to erasure)
CONVERSATIONS_DELETEPermanently deleting chats and conversations, with their messages and media
CHANNELS_MANAGEConnecting and disconnecting channels; listing them; syncing WhatsApp templates
NOTES_WRITEInternal team notes - both writing them and seeing them at all
PII_READUnmasked customer identity: names, phones, emails, avatars, author, location, field values
TEAM_READListing org members
TEAM_MANAGEAdding, re-roling and removing members
CONTENT_READReading Help articles, News posts, and their folders, categories and tags
CONTENT_MANAGECreating, editing, publishing and deleting that content
PARTNER_ADMINTech providers only: provisioning client orgs, the fan-out webhook, the usage rollup

Scopes are independent. A key with MESSAGES_SEND but not MESSAGES_READ can send and never read.

The two destructive scopes are deliberately separate from the manage scopes they sit next to:

  • CONTACTS_ERASE is not part of CONTACTS_MANAGE - a key that maintains contacts day to day should not be able to destroy them.
  • CONVERSATIONS_DELETE is not part of CONVERSATIONS_MANAGE - triaging the inbox and deleting it permanently are different jobs.

Grant each only to the integration that actually needs it. Both are irreversible, and some operations need both: deleting a conversation with removePeople: true is a GDPR erase and requires CONVERSATIONS_DELETE and CONTACTS_ERASE.

PARTNER_ADMIN is the one scope that only means something on a tech provider org (kind TECH_PROVIDER) - it gates provisioning client orgs, managing the fan-out webhook, and reading the usage rollup. On any other org's key it unlocks nothing, including on a PARTNERSHIP distribution partner.

Members are not scoped. A human logged into the dashboard holds every scope implicitly - their authority comes from their role. Scopes exist to constrain machines.

Pick the Narrowest Set

A key is a long-lived credential on a live customer inbox. Mint one key per job:

The integration…Scopes
Mirrors conversations into your product, read-onlyMESSAGES_READ
Mirrors and repliesMESSAGES_READ, MESSAGES_SEND
Replies and triages the inboxadd CONVERSATIONS_MANAGE
Syncs a CRM both waysadd CONTACTS_READ, CONTACTS_MANAGE, PII_READ
Handles data-deletion requestsadd CONTACTS_ERASE
Deletes conversations permanentlyadd CONVERSATIONS_DELETE
Manages the web widget or connects client channelsadd CHANNELS_MANAGE
Publishes Help or News contentadd CONTENT_READ, CONTENT_MANAGE
Provisions client orgs (tech providers)add PARTNER_ADMIN

PII Masking

Customer identity is withheld from any key that does not hold PII_READ. The request still succeeds - the fields simply come back null, and participant lists come back empty.

What PII_READ gates:

  • Names, phone numbers, email addresses, avatars
  • Chat participants and message authors
  • Location payloads
  • People-field values

Masking is not an error, and a masked read is indistinguishable from an unset value. If identity fields are unexpectedly null, check the key's scopes before assuming the data is missing.

Filters Are Refused, Not Just Masked

A masked key cannot use identity as a filter either - otherwise the filter itself would be an oracle on the data the masking hides.

Attempt without PII_READResult
A rule on contact name, assignee, or a person profile field403
Free-text contact search403
Reading a collection's rule values for those fields, or for message textReturned as null
ChatCollection.createdBynull

This is why a masked key sees a collection it cannot reconstruct: the collection still works and still filters, but the values inside its rules come back empty. The filtering happens server-side on data the key may not read.

Internal Notes

Internal notes are team-only messages that never reach the customer. They are invisible to a key without NOTES_WRITE - not redacted, absent: excluded from reads, from previews, and their media attachments are hidden too.

With NOTES_WRITE, a key can read notes and post them by sending a message with internal: true, or by adding a note to a conversation.

There is no separate notes-read scope - NOTES_WRITE gates both directions. Without it:

  • notes, system rows and internal actions are absent from reads;
  • a message-text rule never matches a note;
  • storing a collection with message-level rules is 403;
  • labelling a note answers not-found.

How Requests Are Checked

In order:

  1. Key valid? Missing, malformed, or revoked → 401. A revoked key stops working immediately.
  2. Target org allowed? X-Wexio-Org naming an org you don't manage → 403.
  3. Scope held? The operation's required scope missing from the key → 403, naming the scope.
  4. PII? Masked in the response when PII_READ is absent, rather than refused.

What Happens on a Downgrade

Nothing is deleted, and nothing needs re-issuing. Access simply stops until the plan is restored.

KeysKept. Every call returns PLAN_FEATURE_NOT_ALLOWED with the feature that is missing: apiAccess, mcp or webhooks.
MCP403 with {"error":"plan_feature_not_allowed","feature":"mcp"}.
Webhook deliveryPaused automatically. The connections themselves are kept.
Inbound webhooksRejected with 400 WEBHOOK_UNAVAILABLE.
Still available on any planListing, viewing, pausing and deleting connections, and reading delivery history.
Needs ProCreating, editing or reactivating a connection.

Upgrading restores everything, with webhook delivery resuming within about a minute.

Because keys survive, a lapsed subscription is a pause rather than a migration. Your integration starts working again on its own once the plan is back - there is no key rotation or reconfiguration to redo.

Managing Keys

Minting, listing and revoking keys is dashboard-only - a member login on the org, under Settings → Webhooks and API → API keys. These operations cannot be called with a key, so a leaked key can never mint another. See API keys.

Store the secret at mint time. The full wx_... value is returned once and never again. Later listings show only keyId, label, scopes, status and last-used time.

To rotate: mint a new key, deploy it, confirm traffic on the new keyId, then revoke the old one.

Realtime Is Webhooks

An API key cannot open a GraphQL subscription. For live updates, register an outbound webhook and receive signed events over HTTP.

On this page