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
| Argument | Type | Required | Description |
|---|---|---|---|
label | String | No | A human name, e.g. "CRM sync - prod". Shown in listings so you can tell keys apart. |
scopes | [OrgApiKeyScope!] | No | The scopes this key carries - 15 are available. See the scope table. |
Returns MintOrgApiKeyOutput!
| Field | Type | Description |
|---|---|---|
id | ID! | The key record's ID. |
keyId | String! | The public half. Use it to revoke. |
plaintext | String! | 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!]!
| Field | Type | Description |
|---|---|---|
id | ID! | The key record's ID. |
keyId | String! | The public half of the key. |
label | String | The name given at mint time. |
scopes | [OrgApiKeyScope!]! | What this key can do. |
status | PartnerApiKeyStatus! | ACTIVE or REVOKED. |
lastUsedAt | DateTime | Last successful authentication. null if never used. |
createdAt | DateTime | When 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
| Argument | Type | Required | Description |
|---|---|---|---|
keyId | String! | Yes | The 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:
mintOrgApiKeywith the same scopes as the old key.- Deploy the new secret and confirm traffic on the new
keyId(watchlastUsedAt). revokeOrgApiKeyon the oldkeyId.
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.