GraphQL API

API Keys

Mint a scoped Org API key, list keys, and revoke one

All three operations are dashboard-only: they need a member login on the org and cannot be called with an API key. That is deliberate - a leaked key must not be able to mint another, or widen its own scopes.

mintOrgApiKey

mintOrgApiKey(label: String, scopes: [OrgApiKeyScope!]): MintOrgApiKeyOutput!

Creates a key and returns its secret. The secret is shown once.

Arguments

ArgumentTypeRequiredDescription
labelStringNoA human name, e.g. "CRM sync - prod". Shown in listings so you can tell keys apart.
scopes[OrgApiKeyScope!]NoThe scopes this key carries - 15 are available. See the scope table.

Returns MintOrgApiKeyOutput!

FieldTypeDescription
idID!The key record's ID.
keyIdString!The public half. Use it to revoke.
plaintextString!The full credential, wx_<keyId>_<secret>. Returned only here, never again.
scopes[OrgApiKeyScope!]!The scopes actually granted.

Example

mutation Mint {
  mintOrgApiKey(
    label: "CRM sync - prod"
    scopes: [MESSAGES_READ, MESSAGES_SEND, CONTACTS_READ, CONTACTS_MANAGE, PII_READ]
  ) {
    id
    keyId
    plaintext
    scopes
  }
}
{
  "data": {
    "mintOrgApiKey": {
      "id": "6716b3aa1c34d0012ab89f01",
      "keyId": "9f3c1a2b4d5e6f70",
      "plaintext": "wx_9f3c1a2b4d5e6f70_kQ8sN1pT4vR7wZ2yB5eH8jL0mQ3sV6x",
      "scopes": ["MESSAGES_READ", "MESSAGES_SEND", "CONTACTS_READ", "CONTACTS_MANAGE", "PII_READ"]
    }
  }
}

Store plaintext in your secret manager immediately. Later listings return only keyId and metadata. If you lose it, mint a new key and revoke the old one.

Think twice before including CONVERSATIONS_DELETE or CONTACTS_ERASE. Both are irreversible, neither is implied by the matching manage scope, and a key that only needs to read and reply needs neither.

Check what you actually got. Read scopes back from the response rather than assuming your request was granted verbatim - that is also the simplest way to confirm a key is as narrow as you intended.

orgApiKeys

orgApiKeys: [OrgApiKeyOutput!]!

Lists the org's keys with metadata. Never returns a secret. Takes no arguments.

Returns [OrgApiKeyOutput!]!

FieldTypeDescription
idID!The key record's ID.
keyIdString!The public half of the key.
labelStringThe name given at mint time.
scopes[OrgApiKeyScope!]!What this key can do.
statusPartnerApiKeyStatus!ACTIVE or REVOKED.
lastUsedAtDateTimeLast successful authentication. null if never used.
createdAtDateTimeWhen it was minted.

The status field's type is named PartnerApiKeyStatus - a leftover from when keys were partner-only. Its values are just ACTIVE and REVOKED, and it applies to every org key. Introspection will show that name.

Example

query Keys {
  orgApiKeys { keyId label scopes status lastUsedAt createdAt }
}

Use lastUsedAt to find keys nobody is using any more - an unused ACTIVE key is pure exposure.

revokeOrgApiKey

revokeOrgApiKey(keyId: String!): Boolean!

Revokes a key. It stops working immediately: the next request with it gets 401.

Arguments

ArgumentTypeRequiredDescription
keyIdString!YesThe keyId, not the record's id.

Returns Boolean! - true when revoked.

mutation Revoke {
  revokeOrgApiKey(keyId: "9f3c1a2b4d5e6f70")
}

Rotating a Key

There is no rotate operation - do it in this order so you never have a gap:

  1. mintOrgApiKey with the same scopes as the old key.
  2. Deploy the new secret and confirm traffic on the new keyId (watch lastUsedAt).
  3. revokeOrgApiKey on the old keyId.

Changing Scopes

Scopes are fixed at mint time - there is no "edit scopes" operation. To widen or narrow a key, mint a replacement with the scope set you want and revoke the old one. That is intentional: a key's authority cannot drift after it has been handed out.

On this page