# How do permissions and identity work when a headless agent that is not a specific user queries company knowledge?


URL: https://getunblocked.com/answers/headless-agent-permissions-and-identity/
Published: 2026-11-09T18:30:00Z
Updated: 2026-11-08T16:35:22Z
Author: Brandon Waselnuk
Categories: Context Engine, Engineering Insights

**Short answer:** Give the agent its own identity, scoped to the narrowest audience that will see its output. Use a service identity (a bot or installation token, or a team token limited to chosen sources) for scheduled jobs. Use a delegated token, which can only reach what both the app and the user can reach, when a person triggers the run. Never reuse one engineer's personal token.

---
Headless agent permissions start from one fact: there is no person at the keyboard, so the agent cannot borrow a login. It needs its own identity, which every source checks. **A headless agent** is an agent that runs in CI, on a schedule or from a webhook, with no human session behind each call. There are two sound ways to give it an identity. A **service identity** is a credential that belongs to the agent itself, such as a bot token, a GitHub App installation token or a team API token limited to chosen sources. A **delegated identity** is a token that acts for the person who triggered the run and can reach only what both the app and that person can reach. Avoid a third way, a copied personal token. To choose between the first two, scope the agent to the people who will read what it writes.

## The two identities the big platforms already give you

A GitHub App can authenticate as itself, as an installation, or on behalf of a user. An app authenticates "as an installation" for automation that runs without user input, and that activity is attributed to the app. It authenticates "on behalf of a user" with a user access token when the app should only do what a specific person could do ([GitHub docs](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app)). The delegated token is an intersection. The app "can only access resources that the user has access to". It also "can only access resources that it has permission to access". Requests made this way "will be attributed to that user," so audit logs list the person as the actor ([GitHub docs](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-with-a-github-app-on-behalf-of-a-user)).

Slack works the same way. Bot tokens are "not tied to a user's identity; they're only tied to your app." User tokens "represent the same access a user has to a workspace," and their writes are "performed as if by the user themselves" ([Slack docs](https://docs.slack.dev/authentication/tokens)).

A nightly flaky-test triage job should run as the app. A run an engineer starts from a ticket can run as that engineer.

| Identity | Acts as | Can reach | Fits |
| --- | --- | --- | --- |
| Service identity | The agent or app | What its token or source list allows | Scheduled jobs, CI, shared outputs. |
| Delegated token | The person who triggered the run | Only what both the app and that person can reach | Runs one person asked for and reads. |
| Copied personal token | Whoever created it | Everything that person can reach | Nothing. Avoid it. |


## Scope to the audience of the output, not the trigger

The hard case is a gap between who can read the sources and who can read the answer. Picture a review agent that runs as a service identity with access to a private security channel. It reads an incident thread there, then posts a helpful PR comment that quotes it. The PR is visible to the whole engineering org. No permission was broken at read time, yet private data reached people who could never open that channel. Keeping source permissions intact all the way to the answer is argued in [how to give agents Slack, Jira and Confluence knowledge](https://getunblocked.com/blog/give-agents-slack-jira-confluence-knowledge/).

The fix is to size the agent's access to its least-privileged reader. If the output lands in a public PR, the agent should read only what every PR reader may see. If it writes to one team's private channel, it can read what that team can read. Delegated tokens solve this cleanly when the trigger and the reader are the same person. Service identities need it done by hand, by picking sources. Listing the sources and deciding the permission boundary are steps one and three of [organizing context for AI agents](https://getunblocked.com/blog/organize-engineering-context-for-ai-agents/).

## What MCP requires of the token chain

When the agent reaches company data through MCP servers, the protocol sets rules for identity. An MCP client makes requests "on behalf of resource owners," and servers "MUST validate that access tokens were issued specifically for them as the intended audience" ([MCP specification](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization)). The spec names the headless case too: `client_credentials` clients act "on their own behalf".

The rule that matters most for agents is about relays. If an MCP server calls an upstream API, "the access token used at the upstream API is a separate token," and the server "MUST NOT pass through the token it received from the MCP client" ([MCP security considerations](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/security-considerations)). Chains of agents make this harder. A 2026 survey lists "recursive delegation accountability" among five gaps that no current technology resolves ([Otsuka et al.](https://arxiv.org/abs/2604.23280), 2026). In practice, a server that forwards your agent's token to GitHub or Slack is a red flag. Each hop should carry its own credential, scoped to that hop.

## How Unblocked handles people and headless callers

For people, Unblocked's [Data Shield](https://getunblocked.com/blog/data-shield/) filters answers by each user's permissions in the source system, so they "only contain source material that the user posing the question is authorized to access." It is off by default and switched on per source, except for Slack, where it is always on and cannot be turned off ([Data Shield docs](https://docs.getunblocked.com/team-settings/data-shield.md)). It supports GitHub, Jira, Confluence, Notion, Teams and more. The docs also list 5 sources it does not cover, such as Datadog and Sentry, which every member sees. Check both lists against what your agent reads.

For headless callers, the choice is between the two API tokens. A Personal Access Token is "scoped to your individual account". Running a shared agent on one is the copied personal token from the table above. A Team Access Token "can access every document in the data sources selected when the token is created" ([API quickstart](https://docs.getunblocked.com/api-reference/quickstart.md)). That makes it a service identity whose scope is its source list, so pick that list with the audience rule above. The agent access log records each call's actor as `Person` or `ApiKey`, with the tool and the MCP client ([agent access logs](https://docs.getunblocked.com/api-reference/security-events/list-agent-access-logs.md)). Token setup for an in-house harness is in [giving a background agent context through an API](https://getunblocked.com/answers/give-an-in-house-background-coding-agent-company-context-through-an-api/). How the engine uses the asker's identity is in [inside the Unblocked context engine](https://getunblocked.com/blog/inside-the-unblocked-context-engine/).

## Questions to settle before the agent goes live

- Who triggers each run, and who reads the output? If they are the same person, use a delegated token. If not, use a service identity sized to the reader.
- Which sources does the agent need? List them, then create a token limited to that list rather than one with full access.
- Do the logs show the right actor? A scheduled job that logs as an engineer is borrowing that engineer's identity.
- Does any hop pass a token through? Each server should get its own token, issued for it alone.
- What happens when a permission changes at the source? Revoke one channel or repo, rerun a known question, and confirm the answer changes.

## Read more

- [How to Give AI Agents Your Slack, Jira, and Confluence Knowledge (Tools Compared)](https://getunblocked.com/blog/give-agents-slack-jira-confluence-knowledge/)
- [How to Organize Context for AI Agents When It Lives Everywhere](https://getunblocked.com/blog/organize-engineering-context-for-ai-agents/)

## Related answers

- [Is an LLM-maintained wiki in git a better fix than a context tool for engineering knowledge?](https://getunblocked.com/answers/llm-maintained-wiki-vs-context-tool/)
- [How do we drive adoption of an AI coding tool from a third of engineering to everyone?](https://getunblocked.com/answers/drive-ai-coding-tool-adoption-across-engineering/)
- [How do I make sure Claude actually calls the context MCP without every engineer editing their CLAUDE.md?](https://getunblocked.com/answers/make-claude-call-context-mcp-without-editing-claude-md/)
- [Atlassian Rovo MCP versus a dedicated context tool for Confluence, Jira and code?](https://getunblocked.com/answers/atlassian-rovo-mcp-vs-context-tool/)
