Credentials and Principals
Vincent Depassier · August 30, 2026
Everything that reaches the API Gateway arrives holding a credential. This page is about what a credential actually is, who it belongs to, and what it does and does not grant.
You manage them in API Gateway → API Keys.
The two-level model
Praxsuite separates who is calling from what they are holding.
Principal — the identity e.g. "Mobile App", "Zapier", "Claude MCP"
|
+--- Credential — a key that proves you are it
+--- Credential — another one, for a different environmentA principal is the actor. It has a code, a display name, a type, and a status.
A credential is one secret issued to that principal. A principal can hold several: one per environment, or one being rotated in while the old one is rotated out.
This split is what makes rotation safe. Revoking a leaked key does not delete the identity, does not touch its permissions, and does not affect any other key the same principal holds. You issue a replacement and the integration's configuration — scopes, filters, tool permissions — is untouched, because that configuration hangs off the credential you are replacing, not off a name somebody has to re-enter.
Principal types
Type | For |
| A backend, a script, an integration, an AI connector |
| One of your app's own users, signed in through the gateway |
| An agent running outside your infrastructure |
| A device |
Service is what you want for anything server-side. EndUser principals are not created by hand — they appear when a person signs in through the gateway's auth endpoints.
Key types, and which one goes where
The prefix is not decoration. It tells you where the key is allowed to live.
Prefix | Name | Where it belongs |
| Server Key | Server-side only — a backend, a job, an MCP connector |
| Publishable Key | A browser bundle, a mobile app, anywhere the public can read it |
A publishable key is public. Treat it as already leaked, because in a frontend bundle it is. That is not a flaw — it is what "publishable" means. The protection comes from what its scopes allow, not from the key staying secret. A pk_live_ key should be able to do exactly what an anonymous visitor to your app may do, and nothing more.
A sk_live_ key is the opposite: it is a bearer secret, and anything holding it can do everything its scopes allow. It never belongs in a repository, in client code, or in a URL.
The MCP endpoint accepts
sk_live_only. A publishable key is rejected there, deliberately.
Credential types
Beyond ordinary API keys, the model carries ClientSecret, SignedToken, ClientCertificate, AgentSecret and DeviceSecret for OAuth-style and edge scenarios. For a normal integration, ApiKey is the one you want.
Lifecycle
A credential is always in exactly one state:
Status | Meaning |
| Works |
| Passed its |
| Turned off deliberately |
| Turned off because it leaked — recorded separately so the audit trail says why |
| Paused |
Anything other than Active answers 403 FORBIDDEN. The same is true if the principal is not active — suspending an identity switches off every key it holds at once, which is the fast lever when you do not yet know which key leaked.
A new key is shown exactly once. Only a one-way hash is stored, so nothing in the platform can show it to you again. If it is lost, issue a new one and revoke the old — there is no recovery path, by design.
Setting ExpiresAt is worth doing even when you have no rotation schedule. An expiry turns "we forgot about that integration" into an outage you notice rather than a key that outlives the project it was made for.
The gateway records LastUsedAt and LastUsedIp per credential. That is the field to check before revoking something nobody remembers creating.
The three permission axes
A credential carries three independent sets of permissions. They are separate because they protect different things.
Axis | Governs | Configured in |
Table scopes | Which tables and columns it may read and write, with row filters and masking | the key's Data tab — see PraxQL Security Model |
Docs scopes | Which folders, spaces and documents it may reach | the key's Docs tab |
Tool permissions | Which MCP tool groups it may call at all | the key's Tools tab |
None of them can widen what another denies. Granting the Docs tool group does not grant access to a document the Docs scopes exclude; allowing a table does not allow a tool that was switched off. A request has to pass every axis it touches.
The practical consequence when debugging a 403: check the axis that matches the operation, not the one that matches the data. An MCP call that cannot read a table might be blocked by the table scope or by the tool group, and the two are configured in different places.
Response caching
A credential can cache its own query responses:
Mode | Behaviour |
| Every request is executed (default) |
| Cached for |
| Cached until the underlying table is written to |
| Invalidated on writes, with the TTL as a safety net |
Defaults are 60 seconds and 500 entries.
WriteInvalidated is the interesting one: it gives you a cache that cannot serve stale data after a write your workspace performed. Its blind spot is data changed by something the gateway did not see, which is why Hybrid exists.
Caching is per credential, so a read-heavy public key can cache aggressively while your backend key does not cache at all.
File URLs
FileUrlMode decides how File and Image columns come back in query results:
Mode | Result |
| A URL through the gateway's |
| A pre-signed, time-limited URL usable directly in an |
Proxy keeps every file access behind the same permission check as the row it came from. SasUrl trades that for URLs a browser can load with no credentials, which is what you need for a public gallery — and exactly what you do not want for private documents, because anyone holding the URL can open it until it expires.
FileSasExpiryMinutes defaults to 60, with a floor of 5 minutes and a ceiling of 7 days. Keep it short: the expiry is the only thing limiting a URL once it has left your page.
Practical guidance
One credential per integration. Shared keys make the audit log useless and turn a rotation into a coordinated outage.
Scope to what the integration does today. A scope is trivial to widen later and impossible to un-leak.
Never let a `pk_live_` key write anything a visitor should not be able to write from the browser console.
Set an expiry even without a rotation plan.
Check `LastUsedAt` before revoking. It is the difference between removing a dead key and taking down production.
Next
PraxQL Security Model — how the data axis is enforced, layer by layer.