The core property
What is enforced, and where
Everything is enforced server-side, by the same code the web app uses. None of it depends on the agent behaving well.Permission keys
Permission keys
Every tool declares the permission it needs. The credential either carries it or the tool is not available.Crucially: listing and calling share the same check. A tool you cannot call is not shown to you at all — there is no shown-then-denied surface for an agent to probe.
Actor scope
Actor scope
Tools acting on a specific employee also verify the subject is in the actor’s scope: the actor themself, their direct reports, or — with
hr:approve — the organization.Never a transitive walk. Being the manager’s manager grants nothing.The same-actor rule
The same-actor rule
An agent acting as you cannot approve your own requests, exactly as you cannot in the app.
Tenant isolation
Tenant isolation
A credential is bound to one organization, and isolation is enforced at the database level. A query with no organization context returns nothing rather than everything.
Read-only credentials
Read-only credentials
A read-only credential has every mutating tool hidden. Not refused — hidden.
Confirm-gated writes
Four operations are consequential enough that an agent must obtain an explicit human “yes” in the conversation before calling them:Everything is audited
Every mutation through MCP writes to the same audit log as a mutation through the web app, attributed to the bound employee, with the before and after values. There is no separate MCP audit trail to check, and no way for an agent to write without leaving one. The employee’s Audit tab shows agent activity alongside everything else.Approvals are audited unconditionally, on both outcomes, and the audit entry is written whether or not the notification succeeded. The audit log is the authoritative record.
OAuth specifics
For connector-based clients:- Scopes are fixed bundles, not free-form permission lists a client can compose.
ontime:readgrants read tools;ontime:readwritegrants the full catalog — intersected with the employee’s own role, so it never exceeds what that person can already do. - PKCE is mandatory.
S256only. An absent challenge method is rejected exactly asplainis, never silently defaulted. - Redirect URIs are matched by exact string equality, not by prefix or substring.
- Refresh tokens rotate on use, and reusing an old one revokes the entire family. A stolen refresh token stops working as soon as the legitimate client refreshes.
- Authorization codes are single-use and stored hashed.
Revoking access
Revoke an API key
API & webhooks → the key → Revoke. Immediate: the next request is refused.
Revoke an OAuth token
Revoke from the admin console, or remove the connector in the client. Server-side revocation is immediate.
Practical guidance
1
Bind narrowly, widen later
Start with a key bound to an employee whose authority matches the task. An agent doing attendance triage does not need payroll.
2
One key per agent
So you can revoke one without breaking the others, and so the last-used timestamp means something.
3
Do not bind an agent to an Admin unless it needs to be
Admin carries all 53 permissions, including
role:write — which lets its holder edit any role, including their own. That is a self-escalation path.4
Review the audit log
Agent writes appear on each affected employee’s Audit tab. Check it after your first week of agent operation, not after an incident.