- Scope it on its own terms. Policies and identity attributes apply to the agent itself, so a read-only reporting agent and a deploy agent are governed separately even when the same team built both.
- Make authority explicit. Every token says whose authority the agent is using: its own, or a specific user’s, granted through a delegation that user can see and revoke. When it acts for a user, policy is the intersection of both.
- Follow every action back to it. Audit rows and session activity name the agent, the user it acted for, and the token it used. Runs of Runlayer-hosted agents add the run ID.
- Turn it off precisely. Revoke one token, one delegation, one connection, or the whole account. No person loses access in the process.
What is an Agent Account?
An agent account is a principal in your Runlayer workspace, alongside users and groups. You can name it in policies, give it identity attributes, attach connectors to it, and let users delegate to it. The agent proves it is that principal with a Client ID and Client Secret issued on creation, and calls MCP tools through the Runlayer proxy with the tokens it mints: an M2M token for its own authority, or an OBO token for a user’s delegated authority.Why agents need their own identity
Teams usually start with one of two shortcuts, and both break down the moment more than one agent exists. The agent runs as the person who set it up. A Slack support agent and a Jira cleanup agent both authenticating as the admin who configured them look identical in every log and every policy decision. Tightening one tightens the other, and the only way to stop either is to lock the admin out. All agents share one service account. Six agents behind one “automation” credential means nobody can say which one touched production, a policy for one applies to all, and rotating the secret takes every agent down at once. An agent account gives each agent a principal of its own, so scope, consent, evidence, and shutdown all land on the agent that earned them. Instances stay distinguishable too: Runlayer-hosted agents carry a run ID on every tool call, and every token an agent mints is registered individually, so one token can be revoked without touching the others, and can be bound to the caller’s key with DPoP.Identity at every layer
Token Types
Agent accounts can obtain two types of access tokens depending on their use case:M2M Token (Machine-to-Machine)
Use M2M tokens when your agent account operates autonomously without a specific user context.- The agent acts under its own authority; the account’s policies are the whole boundary
- Only agent account-level policies are enforced
- Ideal for background jobs, scheduled tasks, and agents nobody is sitting in front of
OBO Token (On-Behalf-Of)
Use OBO tokens when your agent account acts on behalf of a specific user.- The agent acts under a user’s delegated authority; that user can see and revoke the delegation
- Intersection of agent account and user policies are enforced
- Requires an active delegation from the user to the agent account
- Ideal for work a specific person asked for, where the audit trail must name both
How OBO Works
The complete On-Behalf-Of flow, end to end: a one-time delegation from the user, a two-step token flow (mint an agent token, then exchange it for an OBO token per RFC 8693), and a proxied MCP call that enforces both parties’ policies and resolves OAuth credentials through session grants. Each concept in the diagram is covered below: authentication, delegations, session grants, and policies. For copy-paste code, see the Agent Account Authentication Recipes.Authentication
Agent accounts authenticate using OAuth 2.0: the Client Credentials grant for M2M tokens, plus an RFC 8693 token exchange for OBO tokens. The token endpoint returns a JWT that you include in theAuthorization header of all API requests. The examples below use Bearer tokens; accounts with Require DPoP on need a proof on every request (see the DPoP recipes).
Getting an M2M Token
resource scopes the token to the server it calls. Accounts with Enforce server scoping on require it.
Getting an OBO Token
To get an OBO token, you need one of:- A user UUID from an active delegation
- A user email from an active delegation
- A WorkOS user access token (RFC 8693 compliant)
Accounts with Strict Mode enabled reject all three of these
subject types. They only accept an id_token minted by the account’s own
login app, and every exchange must name its target servers via
resource.client_credentials (the same call as the M2M example above), then exchange it for an OBO token via RFC 8693 token exchange. The agent token is cacheable and can be reused across many user exchanges, and the client_secret never travels on the per-user exchange call. Pass the same resource on both calls: an exchange can’t widen past the agent token’s scope.
subject_token form that matches what your system stores:
Scoping a token to specific servers (optional)
Pass one or more RFC 8707resource parameters (the MCP server URL) on the
client_credentials or token-exchange call to scope the minted token to those
servers. The server records the targets in the token’s aud claim:
resource:
https://your-runlayer-instance.com/api/v1/proxy/plugins/<plugin-id>/mcp, or
https://your-runlayer-instance.com/mcp for the Runlayer Plugin. The token then
works on that plugin’s endpoint only, not on the servers behind the plugin, which
it reaches through the plugin. You can mix plugins and servers in one request.
When enforcement is on, scope the client_credentials agent token to the same
plugin before exchanging it: an exchange can’t widen past the agent token’s
scope. The agent token can’t call the plugin by itself, because plugins serve
tools as a user; only the exchanged token can.
Strict Mode accounts can’t scope a token to a plugin yet: a
request that names a plugin gets invalid_target.
The
aud claim is narrowed to the servers and plugins the principal can access.
Whether the proxy enforces it is controlled per agent account by the
Enforce server scoping setting — see
Resource Scoping Enforcement. Without
enforcement, omitting resource mints a token with no aud, accepted
everywhere the principal’s policies allow (unchanged behavior).Calling MCP Tools
Once authenticated, you can call MCP tools through the Runlayer proxy using your access token. The proxy exposes a standard MCP Streamable HTTP transport endpoint at/api/v1/proxy/{server_id}/mcp.
Resource Scoping Enforcement
Scoping a token records the target servers or plugins in the token’saud claim. Whether the proxy enforces that scope is a per–agent-account setting, Enforce server scoping, in Settings → Agent Accounts → (account) → Settings.
When enforcement is on, the proxy rejects an aud-bearing token used against a server or plugin outside its aud with 403 invalid_target. This confines a leaked or over-shared token to exactly the servers or plugins it was scoped to, below the account’s usual policy ceiling.
- Off by default — opt in per account. Enforcement is disabled until an admin turns it on for a specific agent account, so no account is forced to scope its tokens by an upgrade.
- When on,
resourceis required. The token endpoint rejects a token request that doesn’t name at least one server or plugin (invalid_target), so every token this account issues is scoped. (Per RFC 8707 §2, an authorization server may require resource indicators.) - Scope a token to every server it will call. A token scoped to server A cannot call server B, even if the account’s policies allow B.
- A plugin scope covers the plugin endpoint only. A token scoped to a plugin works on that plugin’s endpoint and is rejected on the servers behind it, which it reaches through the plugin.
- Aggregate endpoints reject scoped tokens. The agent-account aggregate MCP and skill endpoints fan out beyond a single named resource, so a scoped (
aud-bearing) token is rejected there.
3f6a2b1e-… (see the scoping example above):
403 response
resource fails at the token endpoint instead:
400 response
Delegations
Delegations allow users to grant agent accounts permission to act on their behalf. When a user creates a delegation to an agent account, the agent account can request OBO tokens for that user.Key Concepts
- Delegator: The user granting permission
- Delegatee: The agent account receiving permission
- Expiration: Delegations can have optional expiration times
- Revocation: Users can revoke delegations at any time
Delegation Flow
- User navigates to the agent account in the Runlayer UI
- User creates a delegation to the agent account
- Agent account exchanges its agent token for an OBO token scoped to the user
- Agent account calls MCP tools with the user’s permissions applied
Recovering from a denied OBO call
When an agent’s OBO token exchange is denied because the end-user has no active delegation (or any of the related recoverable failure reasons), the400 invalid_grant response carries an X-Runlayer-Connect-URL header pointing at the agent account’s recovery page:
- The agent forwards the URL from the header to the end-user it is acting on behalf of.
- The user opens the URL and signs in to Runlayer.
- The user clicks Grant access on the agent account page. This creates the delegation and any required session grants for OAuth-protected servers in a single click.
- The agent retries the OBO token exchange — it now succeeds.
The body of the 400 response is unchanged across all denial reasons — the recovery hint is delivered exclusively through the response header. Agents that don’t yet inspect the header continue to work; they just fall back to surfacing the raw
detail message to the user.Session Grants
Session grants control how an agent account authenticates to OAuth-protected MCP servers. A session grant shares a user’s OAuth credentials for a specific server with an agent account, independent of delegations.Personal vs Shared
Credential Resolution
When an agent account makes an OBO call to an OAuth-protected server, Runlayer resolves credentials in this order:- Caller’s personal grant — if the OBO caller has their own session grant for this server, their OAuth credentials are used.
- Shared grant fallback — if no personal grant exists, a shared session grant (from any grantor) is used.
- Error — if neither exists, the call fails with a
401error.
Lifecycle
Session grants are created when:- A user connects to an OAuth connector attached to an agent
- An admin creates one through the Agent Accounts API
If the grantor’s OAuth session expires, calls relying on that session grant will fail until the grantor re-authorizes the server.
Example: Slack Agent with Multiple Users
Suppose you have an agent account called “Support Bot” that needs to call a Slack MCP server on behalf of users.- Alice connects to the agent and authorizes Slack. This creates a personal session grant for Alice.
- An admin promotes Alice’s grant to shared, so it can serve as a fallback for other users.
- Bob connects to the agent but does not authorize Slack — he has no personal grant.
- When Support Bot makes an OBO call to Slack as Alice, Runlayer uses Alice’s personal grant (her own OAuth credentials).
- When Support Bot makes an OBO call to Slack as Bob, Runlayer falls back to Alice’s shared grant (since Bob has no personal grant).
- If Alice later authorizes a second user Carol, and Carol creates her own personal grant, Carol’s OBO calls use her own credentials — Alice’s shared grant is not used.
Strict Mode
By default, the OBO exchange trusts the agent’s claim of which user it acts for — a user UUID or email is enough, as long as a delegation exists. Strict mode (a per-account toggle in Settings → Agent Accounts → (account) → Settings) makes the agent prove it: the user must actually sign in and consent through a login app owned by that agent account, and only the resulting id_token is accepted as the exchange subject. Enabling strict mode provisions a dedicated OAuth login client for the account (public, PKCE-only). Register your app’s redirect URIs when enabling; the loginclient_id is shown read-only in the account dialog. Signing in through it doubles as consent — approving creates the delegation.
What changes at the token endpoint
Session grants at mint
For each OAuth-protected server named inresource:
- Covered — the delegator has an active personal grant, or an active shared grant exists → the exchange proceeds; the earliest grant expiry clamps the token lifetime.
- Uncovered, delegator has a live OAuth session with the server → a personal grant is auto-created for exactly that server at mint.
- Uncovered, latest grant was revoked →
invalid_target(“no active session grant for the requested resource”). Revocation sticks — no silent re-create; the user must re-grant explicitly (e.g. re-Connect on the agent-account page). - Uncovered, no OAuth session →
invalid_target(“the delegator has no valid OAuth session for the requested resource”). The user must authorize the server first.
resource is enough.
Rolling refresh
Every refresh of a strict account’s OBO refresh token revokes the delegation and creates a fresh one starting at that moment — activity keeps the consent alive; revoking the delegation kills the whole chain. The replacement window follows the account’s delegation-lifetime setting. Two consequences:- Any other refresh token bound to the old delegation dies with it: one refresh-token chain per (agent, user) is the intended shape.
- Refresh never auto-creates session grants. If a covering grant was revoked since issuance, the refresh fails with
invalid_targetuntil the user re-grants.
Recipe: strict-mode OBO end to end
Step 1 — the user signs in through the account’s login app (standard OIDC authorization-code + PKCE; the consent screen creates the delegation on first approval):Verifying the id_token. It is a standard OIDC ID token signed with ES256 (P-256). Verify it against the JWKS at the
jwks_uri advertised by https://your-runlayer-instance.com/.well-known/oauth-authorization-server.$AGENT_TOKEN and $RESOURCE as in Getting an OBO Token):
$OBO_TOKEN as usual. For long-running agents, add scope=offline_access plus client_id/client_secret to the exchange to also receive a refresh token (subject must still be the id_token).
Connectors for agent accounts
A recurring question when wiring agents up is how to model connectors for an agent account: whether the agent account needs its own identity in your IdP, whether a single connector can authenticate both as a user and as a service, and how many connectors you end up managing. This section answers those.The agent account is the identity
An agent account is an identity native to Runlayer. Creating one issues aclient_id / client_secret (see Authentication). You do not create a matching user or service account in Okta / Entra for each agent account, and Runlayer does not provision one for you. The agent’s identity lives in Runlayer; your IdP is unchanged. What still lives upstream is the credential the connector uses to reach the SaaS (an OAuth grant or a service token) — that is a property of the connector, not of a separate IdP identity per agent.
One connector authenticates one way
A connector’s authentication method is fixed at configuration time — Auto, OAuth 2.1, Bearer Token, or None — and a single connector authenticates exactly one of those ways. There is no connector setting that accepts both OAuth and a static bearer/API-key at once. So to reach the same upstream SaaS both as a user (OAuth / on-behalf-of) and as an autonomous service (a static service credential), configure two connectors pointed at the same MCP endpoint — one OAuth 2.1 connector and one Bearer Token connector. This is the expected pattern, not a workaround.
For a Bearer Token / API-key connector, the static credential is entered in the connector’s configuration (headers or environment variables) and is field-level encrypted at rest. How you mint that upstream credential (a Jira API token, a Slack app token, a Salesforce connected-app secret, etc.) is vendor-specific and happens in the SaaS, exactly as it would without Runlayer.
Avoid connector sprawl — scope with policy, not with more connectors
You do not need one connector per agent account. A single service-credential connector can back many agent accounts; differentiate what each agent may do with per-agent-account policies (least privilege), not by cloning connectors. In practice the ceiling for one upstream service is two connectors (one OAuth, one M2M), each reused across every agent that needs that service. To scope a read-only agent against a service that also has write access, keep one connector and restrict the agent account’s policy to read tools — you do not need a separate “read-only” connector. Creating an additional connector is only warranted when you genuinely need a different auth method or a different set of static credentials for the same endpoint.When an agent account is attached to an OAuth connector, the connector’s
credential mode — Each user’s own account (each caller’s personal
session grant) vs Always your account (a single shared grant) — decides
whose upstream credentials the agent uses. See
Session Grants → Personal vs Shared.
Autonomous agents that should never borrow a user’s identity should use a
dedicated Bearer Token / service-credential connector instead.
Policies
Both agent accounts and users can have policies that control what actions they can perform. When an agent account uses an OBO token, the effective permissions are the intersection of:- The agent account’s policies
- The user’s policies
- Any server-level policies
Identity Attributes
Agent accounts carry an optional set of identity attributes — key/value pairs (for exampleteam: engineering) that administrators manage in the agent account’s settings dialog or via the API. Unlike user attributes, which sync from your identity provider, agent attributes are set directly by admins, and every change is recorded in the audit log.
Agent and user attributes share one attribute namespace: a policy that targets an attribute (as a principal or in a condition) applies to any user or agent account carrying it. Assigning an attribute to an agent therefore immediately places it in scope of every existing policy targeting that attribute — this is the intended way to scale policies beyond per-agent lists.
Attribute keys registered under Settings → Workspace → Permission Attribute Selectors appear as suggestions in the editor, but any key is allowed; the registry only drives policy-builder dropdowns and is never used for validation. Agents with no attributes are unaffected by attribute-based policies.
Best Practices
Secure your client secret
Secure your client secret
Store your agent account’s client secret securely (e.g., environment variables, secrets manager). Never commit it to version control or expose it in client-side code.
Use OBO when a person asked for the work
Use OBO when a person asked for the work
If a specific user requested the work, mint an OBO token for that user. The token carries both identities, so policy is the intersection of both and the audit row names both. Use M2M only when the agent acts on its own schedule under its own authority.
Handle token expiration
Handle token expiration
Access tokens are valid for 1 hour. Implement token refresh logic in your application to request new tokens before expiration.
Apply least privilege
Apply least privilege
Configure your agent account’s policies to only allow the minimum permissions required for its function. Avoid granting broad access.
Managing Agent Accounts
The lifecycle is provision, rotate, revoke, deprovision. Create the account and hand out its credentials, rotate the secret on a schedule or on suspicion, revoke a single token or all of them when something looks wrong, disable the account to reject every token on its next request, and delete it to revoke all tokens and delegations at once. Agent accounts are managed through the Runlayer UI:- Navigate to Settings → Agent Accounts
- Create new agent accounts with the Add Agent Account button (admin-only)
- Configure agent account settings — including Enforce server scoping — policies, and delegations
- View agent account activity in the audit logs
edit covers the name, the description, and disabling, and delete covers deletion. Creating accounts, rotating credentials, managing connections, and changing any other setting still require the administrator capability.
All workspace members can view agent accounts, create delegations, and create session grants. Only administrators can create agent accounts, rotate credentials, manage connections, or change settings beyond the name, description, and disabling. Under the unified policy engine, the owner can also rename, disable, and delete their account, and a policy can grant those actions per account.
Credential Rotation
Agent account credentials should be rotated periodically or after security events to maintain security.When to Rotate
- Scheduled rotation: Rotate credentials every 90 days as a best practice
- Security incident: Immediately rotate if credentials may have been exposed
- Personnel changes: Rotate when team members with access leave the organization
- Suspicious activity: Rotate if you detect unusual API usage patterns
How to Rotate
- Navigate to Settings → Agent Accounts
- Select the agent account to rotate
- Click Rotate Credentials
- Confirm the rotation
- Save the new client secret immediately (shown only once)
- Update your application with the new credentials
Agent accounts linked to an Agent cannot be rotated from the Agent Accounts page. The UI will direct you to the agent’s page instead, where you can rotate the credentials for the linked account.
Impact of Rotation
When you rotate credentials:- Old client secret is immediately invalidated - The agent account cannot authenticate with the old secret to obtain new tokens
- Existing tokens remain valid until expiry - Previously issued JWTs (both M2M and OBO) continue to work for up to 1 hour unless you revoke them
- Delegations remain intact - No need to recreate delegations
- Policies are unchanged - Access rules continue to apply
Revoking Tokens
Every M2M and OBO access token Runlayer mints is registered at issuance and checked on every request, so revocation takes effect on the token’s next use — you don’t have to wait for the token to expire.Revoke a single token
Send the token to the RFC 7009 revocation endpoint with the agent account’s client credentials:200: unknown, already-revoked, or another account’s tokens are silent no-ops.
A revoked access token is rejected everywhere it is accepted — proxy calls, API calls, and the RFC 8693 exchange (a revoked M2M token can no longer be exchanged for new OBO tokens). Each revocation is recorded in the audit logs.
Access and refresh tokens are revoked independently: revoking an OBO access token does not revoke the refresh token that produced it, and revoking a refresh token does not revoke access tokens already issued. Revoke each token, or disable the agent account to stop everything at once.
Revoke everything
- Disable the agent account (Settings → Agent Accounts): all of its tokens stop working immediately and no new tokens can be minted. Re-enabling restores unexpired tokens.
- Delete the agent account: permanently revokes all outstanding access and refresh tokens and all delegations.
Error Handling
When authenticating or making API calls, you may encounter these common errors:Authentication Errors
OBO Token Exchange Errors
Proxy Call Errors
Example Error Responses
Agent account authentication and token exchange errors follow RFC 6749 §5.2: a JSON object witherror and error_description fields (a legacy detail field is also emitted for backward compatibility):
X-Runlayer-Connect-URL header: