Skip to main content
An agent account is the identity Runlayer gives an agent. Instead of running under a person’s login or a shared service credential, the agent is its own principal in your workspace, with its own policies, its own delegations from the people it works for, its own tokens, and its own audit trail. That changes what you can do with an agent:
  • 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 the Authorization 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:
  1. A user UUID from an active delegation
  2. A user email from an active delegation
  3. 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.
Getting an OBO token is a two-step flow: mint an agent token with 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.
For step 2, pick the subject_token form that matches what your system stores:
The agent now acts with the intersection of agent and user policies.

One-call exchange with client credentials

You can also skip step 1: authenticate the token-exchange call with the account’s client_id and client_secret and leave out actor_token and actor_token_type. The exchange is checked exactly like the two-step flow: same subject types, same resource rules, same delegation, session-grant and Strict Mode checks.
Use the one-call exchange for OAuth-protected servers when the account has Enforce server scoping or Strict Mode on. An agent token minted without a user can’t be scoped to a server that uses per-user OAuth, so step 1 of the two-step flow fails with invalid_target for those servers. The one-call exchange scopes the OBO token to the server directly, because the user’s session grant supplies the credential.
Migrating from the client_credentials OBO shortcut: earlier versions documented passing subject_token / subject_token_type directly on the client_credentials grant. That shape is still accepted for backward compatibility but deprecated — switch to the two-step flow: mint an agent token with client_credentials, then exchange it with grant_type=urn:ietf:params:oauth:grant-type:token-exchange (agent token in actor_token, user identity in subject_token, per RFC 8693 §2.1).

Scoping a token to specific servers (optional)

Pass one or more RFC 8707 resource 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:
The same parameter works on the token-exchange call. To scope an on-behalf-of (OBO) token to a plugin, pass the plugin’s MCP URL as 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 scope a token to a plugin too; see Session grants at mint for how the servers behind it are covered.
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).

Using an API key

For clients that cannot run the client credentials flow, an admin can create long-lived API keys from the agent account page: open the ⋯ menu and choose Manage API keys. A key authenticates as the agent account on the per-server MCP proxy URLs (/api/v1/proxy/{server_id}/mcp) and is shown once at creation. The agent-account aggregate endpoint (/api/v1/proxy/agent-account/{agent_account_id}/mcp) does not accept API keys; use an access token there.
A key carries no DPoP binding and no audience, so keys are unavailable while Require DPoP, Strict mode, or Enforce audience is on. Turning one of these on stops existing keys immediately. Disabling or deleting the agent account stops its keys too. Revoke a key from the same dialog; rotate by creating a new key, updating the client, then revoking the old one.

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.
The proxy endpoint uses the MCP Streamable HTTP transport. You can also use any MCP SDK client (such as the Vercel AI SDK, OpenAI Agents SDK, or Google ADK) pointed at the https://your-runlayer-instance.com/api/v1/proxy/{server_id}/mcp URL.

One URL for all of an agent account’s connectors

An autonomous agent doesn’t need to know every connector’s ID. Each agent account has one MCP endpoint that serves every hosted connector the account is connected to:
It exposes two tools, search_tools and execute_tool, which find and call tools across those connectors. Connect the account to a new connector and the agent picks it up with no code change. The agent account ID is in the URL of its page under Settings → Agent Accounts.
  • Agent tokens only. It accepts M2M and OBO access tokens, not API keys.
  • Not with Enforce server scoping. With enforcement on, every token is scoped with resource, and this endpoint rejects scoped tokens. See Resource Scoping Enforcement.
  • M2M tokens skip OAuth connectors. OAuth connectors only appear with an OBO token; the search_tools description names the ones that were skipped. This includes OAuth connectors whose owner enabled standalone access on their shared grant: those still work with an M2M token at the per-connector URL (/api/v1/proxy/<server_id>/mcp). Bearer-token connectors work with M2M tokens.
The Runlayer Plugin URL (https://your-runlayer-instance.com/mcp) and other plugins serve tools as a user. An M2M token gets 403 Plugin proxy requires user context there. Use the agent account URL above, or exchange the token for an OBO token.

Long-running tool calls

Runlayer waits up to 104 seconds for the upstream server to answer one tool call. Your MCP client may give up sooner. The TypeScript MCP SDK (@modelcontextprotocol/sdk) stops waiting after 60 seconds by default, so a slow query (for example a large Snowflake SELECT) fails on the client at 60 seconds while Runlayer is still waiting for the result. Raise the client’s timeout to just under Runlayer’s:
A call that needs more than 104 seconds fails on Runlayer’s side whatever the client allows. Make the query cheaper, or split it into smaller calls.

Where an agent’s calls show up

A tool call made with an agent account’s token is logged with the agent account as the actor. For an OBO token, the same audit row also names the delegating user. So these calls don’t appear under a connector’s My activity tab, which lists only calls where you are the actor. To see them:
  • Open the agent account under Agent accounts and check its Activity section (requires permission to manage agent accounts; the account’s calls appear there only to admins who can view the organization’s audit logs).
  • Or filter Audit Logs by the agent account (requires permission to view the organization’s audit logs).

Resource Scoping Enforcement

Scoping a token records the target servers or plugins in the token’s aud claim. The proxy enforces that scope: an aud-bearing token used against a server or plugin outside its aud is rejected 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. Tokens minted without resource carry no aud and are not affected. A scoped agent token keeps its scope through an on-behalf-of exchange: the exchange must name a resource inside the agent token’s aud, or it fails with 400 invalid_target. Self-hosted operators can turn both checks off for a deployment with FF_DISABLE=agent-token-audience-enforcement; deployments still on WorkOS feature flags turn it on with FF_ENABLE=agent-token-audience-enforcement. Accounts with Enforce server scoping on stay enforced either way. The per–agent-account setting Enforce server scoping, in Settings → Agent Accounts → (account) → Settings, makes scoping mandatory: the token endpoint rejects a token request for that account that doesn’t name at least one server or plugin (invalid_target), so every token it 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.
For example, with a token scoped to server 3f6a2b1e-… (see the scoping example above):
403 response
A token request that omits resource fails at the token endpoint instead:
400 response
Turning enforcement on takes effect immediately, including for tokens already issued: any aud-bearing token used outside its aud starts getting rejected, and new token requests must include resource. Confirm your clients pass every server they call before enabling.

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 don’t expire unless the user sets an expiry or the account caps it (see Expiry and lifetime limits)
  • Revocation: Users can revoke delegations at any time

Delegation Flow

  1. User navigates to the agent account in the Runlayer UI
  2. User creates a delegation to the agent account
  3. Agent account exchanges its agent token for an OBO token scoped to the user
  4. Agent account calls MCP tools with the user’s permissions applied
Delegations control who the agent account can act as. For OAuth-protected servers, the agent account also needs a session grant to determine whose credentials to use (see below).

Expiry and lifetime limits

By default a delegation never expires: it lasts until the user revokes it.
  • The user sets their own expiry. After Grant access, the line under Connectors on the agent account page reads Access does not expire or Access expires with a date. Click it to choose No expiration, For 1 hour, For 1 day, For 1 week or For 30 days. Through the API, send expires_at when creating the delegation (POST /api/v1/agent-accounts/{id}/delegations) or change it later (PATCH /api/v1/delegations/{id}). Only the delegating user can do either.
  • Admins cap it per account. Max delegation lifetime in Settings → Agent Accounts → (account) → Settings (1 hour, 24 hours, 7 days, 30 days or No limit; max_delegation_lifetime_seconds in the API, 60 seconds to 10 years) applies to new delegations. Once a cap is set, a delegation created without an expiry expires when the cap runs out. Asking for a longer expiry, or removing the expiry, is refused with 400, and the user’s menu hides No expiration and any duration over the cap. Changing an expiry restarts the cap from the moment of the change. Existing delegations keep their expiry until someone changes it.
  • Admins set the token lifetime. Access token lifetime, in the same settings (15 minutes, 1 hour, 8 hours, 24 hours or Default, which is 1 hour; access_token_lifetime_seconds in the API), sets how long tokens minted for the account live, including tokens for runs of a Runlayer-hosted agent. An OBO token is also cut short so it never outlives its delegation.
When a delegation expires or is revoked, the agent’s next OBO exchange for that user fails with 400 invalid_grant and the recovery header described below, and a token already issued is refused on its next call with 403 Delegation is no longer active (revoked or expired). Changing these settings needs the edit_security action on the account (see Managing Agent Accounts).

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), the 400 invalid_grant response carries an X-Runlayer-Connect-URL header pointing at the agent account’s recovery page:
Recommended flow:
  1. The agent forwards the URL from the header to the end-user it is acting on behalf of.
  2. The user opens the URL and signs in to Runlayer.
  3. 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.
  4. The agent retries the OBO token exchange — it now succeeds.
The header is only set when the failure is recoverable by the end-user. Agent-side failures (disabled agent account, missing/invalid client credentials, bad agent JWT) deliberately omit the header so callers cannot probe for the existence of agent accounts they do not control.
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

Switching a connection between Each user’s own account (personal grants) and Always your account (a shared grant) can be disruptive: calls that relied on the previous grant can start failing, and users without a personal grant may need to grant the agent access to the server again.

Credential Resolution

When an agent account makes an OBO call to an OAuth-protected server, Runlayer resolves credentials in this order:
  1. Caller’s personal grant — if the OBO caller has their own session grant for this server, their OAuth credentials are used.
  2. Shared grant fallback — if no personal grant exists, a shared session grant (from any grantor) is used.
  3. Error — if neither exists, the call fails with a 401 error.

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
An admin does not need their own account on an OAuth connector to add it to an agent account. Adding it only grants the agent access to the connector. Each user who should be acted for then clicks Grant access (connecting the connector first if needed), which attaches their own credentials. Until a user has a personal grant, or a shared grant exists, the agent’s calls to that connector as that user fail with 401. If the admin who adds the connector holds a live OAuth session for it, their own grant is attached too. Revoking a delegation through the Runlayer UI does not revoke session grants, and revoking a session grant does not revoke delegations. However, disconnecting from an agent (removing your connection) revokes both your delegation and your session grants for that agent. Additionally, when an admin deactivates a user, the user can no longer obtain new OBO tokens, effectively rendering their session grants unusable — though the grants themselves are not explicitly deleted from the system.
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.
  1. Alice connects to the agent and authorizes Slack. This creates a personal session grant for Alice.
  2. Alice switches her grant to shared (Always your account), so it can serve as a fallback for other users. Only the grantor can share a grant; an admin can’t share someone else’s.
  3. Bob connects to the agent but does not authorize Slack — he has no personal grant.
  4. When Support Bot makes an OBO call to Slack as Alice, Runlayer uses Alice’s personal grant (her own OAuth credentials).
  5. 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).
  6. 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 login client_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 in resource:
  • 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.
A plugin named in resource reaches the OAuth servers that plugin serves the user (for the Runlayer Plugin, every hosted server the user can access). A server already covered by a grant clamps the token as above, but the exchange never creates a grant for a server it only reaches through a plugin, and an uncovered one doesn’t fail the exchange. Calls to an uncovered server through the plugin are refused (or prompt the user to authorize) until the user grants it, by naming it in resource once or connecting it on the agent-account page, while the plugin’s other servers work. Servers that don’t use OAuth need no grant — naming them in 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 for a server named in resource was revoked since issuance, the refresh fails with invalid_target until the user re-grants. A server reached through a plugin is left out instead, as at mint.

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.
Step 2 — exchange the id_token for an OBO token, naming every server the token will call ($RESOURCE as in Getting an OBO Token). This uses the one-call exchange, which works for OAuth-protected servers too; with an $AGENT_TOKEN as actor_token instead, step 1 can’t be scoped to an OAuth-protected server under Strict Mode:
Step 3 — call MCP tools with $OBO_TOKEN as usual. For long-running agents, add scope=offline_access to the exchange to also receive a refresh token (subject must still be the id_token).
Flipping strict mode on takes effect immediately for new exchanges: callers still presenting user-id/user-email/WorkOS-access-token subjects, or omitting resource, start failing at the token endpoint. Existing delegations and refresh tokens are grandfathered, but every strict refresh rolls its delegation from then on.

Agents on runtimes that can’t host a callback

Some runtimes can’t serve the login app’s redirect_uri, for example a custom agent on Google Agent Runtime, a serverless function, or a batch job. Run the login leg in a small web service you host instead, a login broker, and give the agent only short-lived OBO access tokens:
  1. Register the broker’s callback URL as a redirect URI when you enable strict mode on the agent account. The URL must not be on your organization’s redirect-URI denylist.
  2. Send the user to the account’s login app (Step 1 above) with the broker’s callback as redirect_uri. The broker exchanges the code, then immediately runs Step 2 with scope=offline_access. It keeps the OBO refresh token and discards the id_token.
  3. Store one refresh token per (agent account, user) in your secret store. Strict refresh keeps one chain per pair (see Rolling refresh), so reuse that token instead of signing the user in again.
  4. When the agent needs to call tools as a user, the broker refreshes the token and hands the runtime the access token:
The refresh needs the agent account’s client_secret. Keep the secret and the refresh tokens in the broker, so the runtime only ever holds access tokens that expire within the account’s access-token lifetime. One broker can serve several agent accounts: each account has its own login client_id, so register the same callback on each and keep track of which account a login started from (for example in state). If a refresh fails with invalid_grant (delegation revoked or expired) or invalid_target (a session grant was revoked), send the user back through the login.

Discovering tools under Strict Mode

Every Strict Mode token is scoped to the servers or plugins it names. To call list_servers on the Runlayer MCP, include resource=https://your-runlayer-instance.com/api/v1/proxy/runlayer/mcp in the exchange and make sure the agent account can access that MCP server. A token scoped only to other connectors or plugins is rejected there with invalid_target. The agent-account aggregate endpoint (/api/v1/proxy/agent-account/{agent_account_id}/mcp) cannot match any token’s aud, so it rejects scoped tokens with invalid_target. To discover connector tools without listing connector IDs in the agent, name a plugin as the resource and let the plugin be the agent’s catalog:
  1. Create a plugin with only the connectors (and tools) this agent should see, and make sure the agent account can use it.
  2. Exchange the id_token with resource=https://your-runlayer-instance.com/api/v1/proxy/plugins/<plugin-id>/mcp.
  3. Point the agent’s MCP client at that same URL. With Dynamic tools disabled, tools/list returns connector tools directly. With Dynamic tools enabled, the default nested presentation instead returns search_tools and execute_tool for discovery and calls; supported clients can receive connector tools directly when flat routing is enabled.
To give the agent another connector, add it to the plugin; the agent’s configuration doesn’t change. The Runlayer Plugin (resource=https://your-runlayer-instance.com/mcp) works the same way, but it lists every connector the user can reach, including ones the agent account isn’t allowed to call, so a dedicated plugin keeps the agent’s view to what it can actually use. OAuth-protected connectors in the plugin still need a session grant from the user. The exchange doesn’t create one for a connector it only reaches through a plugin (see Session grants at mint), so have the user connect those connectors once on the agent account page.

Connectors for agent accounts

Who can add a connector

A connector on an agent account is three things, each written by its own endpoint:
  1. A policy allowing the account on the connector (POST /api/v1/policies with the account as an agent principal and the connector in scope.servers). Security policy administrators write it as any other policy. Agent account administrators, the account’s owner, and anyone granted edit_permissions on the account under the unified policy engine can write it too, for that account only. Without administrator rights over the connector, the tools written are limited to the ones you can use yourself: All tools is saved as that list, and a tool you cannot use is refused. Adding a connector the account already has updates its tools instead of adding a second policy.
  2. Your delegation to the account, if you have none (POST /api/v1/agent-accounts/{id}/delegations).
  3. Your session grant on an OAuth connector (POST /api/v1/agent-accounts/{id}/session-grants), once you have connected your own account to the connector.
Add connectors on the agent account page runs these steps in order. Each step can be repeated safely, so retrying after a failure finishes what is missing. Changing a connector’s tools updates its policy. Remove connector deletes its policy and revokes your own session grant. A policy that also covers other agent accounts or connectors is managed on the Policies page. Under the unified policy engine, use on an agent account also lets you create and revoke your own delegation and session grants for it. 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 a client_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
This ensures that an agent account acting on behalf of a user can never exceed either party’s permissions.

What counts as the agent account’s policies

The agent’s side of that intersection is checked on every tool call, whichever URL the token is used on: the agent account URL, a connector URL, or the Runlayer Plugin (/mcp). A connector that no allow rule grants the agent account is denied. That side counts every allow rule that matches the agent account, not only the connectors listed on its page:
  • Rules that name the account. These are the account’s connectors.
  • Rules for Everyone ({"type": "any"}), which match users and agent accounts alike.
  • Rules for every agent account ({"type": "agent", "ids": ["*"]}).
  • Attribute rules whose attributes the account carries (see Identity attributes).
Group and role rules never match an agent account. So if a connector is allowed for Everyone, every agent account can call it, even when it isn’t on the account’s page. For an OAuth connector the call still needs the user’s session grant for that account. A connector with shared credentials (API key, bearer token, or no auth) needs nothing else. To keep an agent account to exactly the connectors on its page:
  • Give broad access to groups instead of Everyone. Group rules reach people, not agent accounts.
  • Or add a deny rule for the agent account on the connectors it must never call. A deny wins over every allow.
  • Point the agent at its agent account URL. It never lists a connector that only an Everyone rule allows, so the agent doesn’t see those. The Runlayer Plugin lists every connector the user can reach.

Limit an agent to a subset of a user’s access

To let an agent do only part of what the person it acts for can do, for a limited time, combine these settings. They work the same for agents you run outside Runlayer (see Agent Platforms).

Identity Attributes

Agent accounts carry an optional set of identity attributes — key/value pairs (for example team: 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 → Sign-in & provisioning → 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

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.
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.
Access tokens are valid for 1 hour unless an admin changes the account’s Access token lifetime, and an OBO token never outlives its delegation. Implement token refresh logic in your application to request new tokens before expiration.
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:
  1. Navigate to Settings → Agent Accounts
  2. Create new agent accounts with the Add Agent Account button (admin-only)
  3. Configure agent account settings — including Enforce server scoping — policies, and delegations
  4. View agent account activity in the audit logs
Every agent account has an owner: the person accountable for it, shown in the account summary when you edit the account. The creator is the owner. An account that backs a Runlayer agent takes that agent’s owner and changes owner when the agent is transferred. Ownership records who is accountable for the account. Under the unified policy engine, the owner can also change every setting of the account, disable and re-enable it, rotate its credentials, and delete it. A policy can grant the same per account, one action at a time. edit covers the name, description, and disabling. edit_security adds every security setting (server scoping, DPoP, strict mode and its redirect URIs, the token and delegation lifetimes) and re-enabling, and needs edit alongside it. rotate_credentials issues a new client secret, which lets its holder act as the account, so grant it, and edit_security, only to someone you would trust with the account’s credentials. Identity attributes stay with administrators: they decide which attribute-based policies the account matches, so no grant or ownership changes them. delete covers deletion. An edit grant alone reaches neither the security settings nor rotation. To keep an owner from changing the security settings or rotating, write a deny on edit_security or rotate_credentials for that account. Creating accounts still requires the administrator capability.
All workspace members can view agent accounts, create delegations, and create session grants. Only administrators can create agent accounts. Without the unified policy engine, only administrators can add or remove connectors, change, rotate, or delete one.

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

  1. Navigate to Settings → Agent Accounts
  2. Select the agent account to rotate
  3. Click Rotate Credentials
  4. Confirm the rotation
  5. Save the new client secret immediately (shown only once)
  6. 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) keep working for the rest of their lifetime — 1 hour by default, or longer if an admin changed the account’s Access token lifetime — unless you revoke them
  • Delegations remain intact - No need to recreate delegations
  • Policies are unchanged - Access rules continue to apply
After rotating credentials, existing access tokens remain valid for the rest of their lifetime — 1 hour by default, or longer if an admin changed the account’s Access token lifetime. If you need to fully block access immediately, revoke the outstanding tokens or disable the agent account in addition to rotating credentials. Update your application with the new client secret promptly, as no new tokens can be obtained with the old secret.

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:
This works for M2M access tokens, OBO access tokens, and OBO refresh tokens — pass the token itself and Runlayer detects the type. Per RFC 7009 the endpoint always returns 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.

Respond to a compromised agent

If an agent starts doing something it shouldn’t, for example after a prompt injection, find which agent it is, stop it, then clean up.

1. Find the agent

  • Audit log: every tool call names the agent account, the user it acted for on OBO calls, and the run for Runlayer-hosted agents. To list everything one agent did, run uvx runlayer logs --all --agent-id <agent account id>. See Querying from the CLI.
  • Sessions: for a Runlayer-hosted agent, open the agent’s Sessions tab. Each run shows its prompts and tool calls step by step.

2. Stop it

1

Stop the live session

In Sessions, open the session, click Stop (or press Shift+K) and give a reason. Every later MCP tool call through Runlayer that carries this session’s ID is denied until someone clears the stop. Over the API:
Security, IT and Super Admins can stop sessions. A stop takes up to 5 seconds to apply and covers this one session only, so continue with the next step to stop the agent everywhere.
2

Cut off the agent

  • An agent account you created: disable it in Settings → Agent Accounts, or send PATCH /api/v1/agent-accounts/{agent_account_id} with {"is_active": false}. Every token it holds fails on its next request, and it can’t mint new ones.
  • A Runlayer-hosted agent: its agent account can’t be disabled on its own. Turn the agent off with the toggle on its page so schedules, webhooks and Slack can’t start it again. Then cancel the running run with Cancel run in the run drawer, or POST /api/v1/agents/{agent_id}/runs/{run_id}/cancel when the run supports cancelling.

3. Clean up

  • Rotate the client secret and revoke outstanding tokens. For a Runlayer-hosted agent, rotate from the agent’s page.
  • Review the delegations users gave the agent and revoke the ones you don’t trust, or delete the agent account to revoke all of its tokens and delegations at once.
  • To keep it blocked while you investigate, add a deny policy for the agent account. From an incident, Block actors writes that policy for the agents involved.
  • When you’re done, clear the session stop with Resume in Sessions, or POST /api/v1/sessions/{session_id}/stop/clear with a reason, and turn back on what you turned off.
Each session stop, clear and token revocation is recorded in the audit log.

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 with error and error_description fields (a legacy detail field is also emitted for backward compatibility):
When the denial can be recovered by the end-user (missing delegation, user not found / inactive, malformed actor token), the same response also carries an X-Runlayer-Connect-URL header:
Check the error_description field for specific guidance on how to resolve the error. When X-Runlayer-Connect-URL is present, forward that URL to the end-user — clicking through and signing in lets them grant the missing delegation in one step. See Recovering from a denied OBO call.