Integration keys
An integration key is a machine credential for a server-to-server caller. It begins cbj_, is bound to exactly one workspace and one explicit scope set, is stored only as a SHA-256 digest, and is shown in full exactly once — at issue. It opens no Owner surface, and it cannot widen its own scope. The public documentation surface accepts no key at all.
What a key is#
Connect's application API is session-cookie authenticated: a browser, a Google sign-in, a person. Another *service* has neither, so the integration key exists to give a server-to-server caller an identity without pretending it is a human session. The design is deliberately narrow rather than general-purpose.
cbj_<43 url-safe characters from 32 random bytes>
Authorization: Bearer cbj_…| Field | Meaning |
|---|---|
key_id | The identifier you refer to the key by. Safe to log |
name | A human label, so a list of keys is readable months later |
key_hash | SHA-256 of the plaintext. The plaintext itself is never stored |
workspace_id | The single workspace the key is bound to |
scopes | The explicit scope list. There is no all scope |
enabled | A key can be disabled without being destroyed |
created_at, last_used_at, revoked_at | Issued when, last presented when, revoked when |
Scopes, and why there is no wildcard#
Scopes are additive and explicit, and the set is closed. Issuing a key with a scope that is not known raises rather than quietly accepting it, and issuing with no scope at all is refused — a key that can do nothing is a credential in circulation with no reason to exist.
| Scope | Grants |
|---|---|
integration:verify | Confirming the key and the workspace it belongs to |
demo:knowledge | The demonstration knowledge capability |
demo:safety | The demonstration safety capability |
demo:draft | The demonstration drafting capability |
The absence of an all scope is the load-bearing decision. If one existed, every future capability would be granted retroactively to every key already issued, and the grant would happen at deploy time with nobody deciding it. Adding a capability to an existing integration has to be a deliberate act.
Issuing, storing, rotating, revoking#
Have a key issued for one integration, named after that integration, with the narrowest scope set that does the job.
Result The plaintext is returned once. It is not recoverable afterwards, because only its digest was kept.
Put the plaintext straight into a secret manager or the deployment's secret store. Never into source control, a configuration file that is committed, a log line, or an issue.
Result The only copy lives where secrets already live, and rotating it is a configuration change rather than a code change.
To rotate, issue the second key first, deploy the new value, confirm
last_used_atis moving on the new key, and only then revoke the old.Result There is no window in which the integration has no working credential. Revoking first and issuing second is the same operation with an outage in the middle.
Revoke by key id when the integration is retired or the value may have been exposed.
Result
revoked_atis stamped and verification fails from that moment. Revoking a key that is already revoked reports that it was not changed, rather than reporting a fresh success.
Verification itself is unremarkable and deliberately so: the presented value must carry the cbj_ prefix, its digest is compared in constant time against the stored hash, and the row must be enabled with no revocation date. A value that fails any of those is simply not a key.
What a key does not open#
It grants no access to the Owner API surface. Owner routes are the operator's own — running the platform rather than using Connect — and a tenant credential reaching them would defeat the separation the middleware allowlist exists to enforce.
It also has nothing to do with the public developer surface documented in this section. /api/public/* holds no session and takes no credential; the MCP endpoint's discovery document states its authentication as "none for the public documentation tools", and there is no authenticated MCP tool to present a key to. If you are reading this because a documentation call returned 401, the problem is elsewhere — those routes do not authenticate at all.
And a key never bypasses the services that own the records. A caller with a scope goes through the same domain service a person's action would, so autonomy rules, approvals, suppression, safe-sales limits, metering and the audit trail all still apply. There is no route that runs SQL and none that proxies an arbitrary internal endpoint.
Questions#
I lost the plaintext. Can it be recovered?
No. Only the SHA-256 digest is stored, which is the same treatment session tokens get. Issue a replacement, move the integration onto it, then revoke the one you lost.
Can one key serve two workspaces?
No. workspace_id is a single value on the key and the scope of everything the key can reach. Two workspaces means two keys, which is also what makes the audit trail readable.
Does a key expire on its own?
The record carries no expiry date — it carries enabled and revoked_at, both of which are acts. Treat rotation as a scheduled task of your own rather than something the credential will remind you about.