Act on behalf of users
Let a tool or agent call an API as a specific person, whether they are in the loop or away, with a short-lived credential per request and the user named in the audit log.
You want your tool or agent to call an API as a specific person: GitHub as the developer who asked, Google Calendar as the person whose meetings they are, Linear as the user who typed the request. Their permissions apply, and the Audit Log names them rather than a shared service account.
Keycard gives your code a short-lived credential for that person on every call. Nothing is stored between calls. Which walkthrough you follow depends on one question: is the user there when the call happens?
This page helps you choose. The steps are in the walkthroughs:
- The user is there: Call External APIs from MCP (you write the server with the SDK) or Access APIs on Behalf of Users (Claude Code builds it)
- The user is away: Act on Behalf of Absent Users
Which case is yours
Section titled “Which case is yours”| The user is there | The user is away | |
|---|---|---|
| When | Chat, IDE and MCP tool calls, or any request the user just made | Scheduled jobs, queue workers, agents that keep running after the user has left |
| What the user does | Signs in. Their token arrives on every request. | Authorizes once, through a page you host. |
| How your code gets the credential | Exchanges the user’s token from the request for an upstream token (delegation) | Authenticates as itself and names the user (impersonation) |
| What the Audit Log shows | The user and your Application on each credentials:issue |
Your Application as the actor, the user as its delegator |
| Walkthroughs | Call External APIs from MCP (SDK), Access APIs on Behalf of Users (Claude Code) | Act on Behalf of Absent Users |
Both cases need the same Console setup: the upstream API added as a Resource, and that Resource declared as a dependency of your Application. Access policy is evaluated on every exchange.
The user is there
Section titled “The user is there”Your tool receives the user’s Keycard token on the incoming request. It exchanges that token for a short-lived token for the upstream API, scoped to that user, and calls the API. The exchange repeats on every call; your server never holds the user’s upstream token between requests.
Two walkthroughs build this. They end in the same token exchange and differ in who writes the code.
Take the SDK path if you already have an MCP server, or want to see every line as you write it. The GitHub and Google Workspace guides finish it with a working server. Take the Claude Code path if you want a running example in the fewest steps and will read the code afterwards.
The user is away
Section titled “The user is away”Nobody is signed in when the job runs, so there is no token to exchange. Instead, the user authorizes your Application once, through a landing page you host. After that, your agent authenticates as itself with a confidential credential, names the user it acts for, and Keycard issues a credential for that user as if they had made the request.
This needs a confidential Application configured with implicit consent, since nobody is there to approve a consent screen, a prior grant from the user, and a Policy that permits the impersonation, which the default policies do not. Without that grant the exchange returns insufficient_authorization; without the Policy it returns access_denied.
What’s next
Section titled “What’s next”- Run on Render without secrets: run the SDK server in production with workload identity in place of a client secret.
- Run on Fly.io without secrets: deploy the Claude-built server the same way.
- Grant an agent access to Snowflake: when the agent should act as itself, not as a user.
- Act as the user who asked: the case for this pattern, and what a security reviewer asks about it.