Skip to main content

What an agent actually is

An agent is a process that calls APIs on behalf of a user, making decisions autonomously. It reads data, takes actions, and chains tool calls together without a human approving each step. Every API call the agent makes requires authentication. When it opens a PR on GitHub, posts to a Slack channel, or updates a Linear ticket, it needs a credential for each service — and that credential must encode who is acting, on whose behalf, with what permissions, and for how long. A typical agent loop looks like this:
# A simplified agent loop
def agent_loop(task: str, tools: list[Tool]):
    plan = llm.plan(task, tools)

    for step in plan:
        # The agent decides which tool to call and with what arguments
        result = step.tool.execute(**step.args)
        # Each tool call hits a real API: GitHub, Slack, Linear, database, etc.
        # The agent needs credentials for every one of these calls
Agents are often confused with two things they are not:
  • They are not users. There is no human at the keyboard approving each action.
  • They are not service accounts. They do not run fixed system logic; they interpret user intent dynamically.
Consider a concrete example. A user says: “Summarize the open issues in my repo and post a summary to #engineering.” The agent needs:
  • GitHub API access to list issues (scoped to that user and repo)
  • Slack API access to post the summary (scoped to that workspace and channel policy)
// What the agent does under the hood
const issues = await github.issues.listForRepo({
  owner: "acme-corp",
  repo: "backend",
  state: "open",
});

const summary = await llm.summarize(issues.data);

await slack.chat.postMessage({
  channel: "#engineering",
  text: summary,
});
Without credentials, the agent can only generate text that a human must copy and paste. With credentials, it can actually take action. This makes agents a fundamentally new kind of principal, one most identity infrastructure was not built to handle.

Why credentials matter

Agents do not just generate text. The moment an agent opens a PR, comments on a review, posts in Slack, updates Linear, or queries a database, it must authenticate to those APIs. A single task can require multiple credentials:
async function handleTask(task: string) {
  // Step 1: Read context from GitHub
  const pr = await github.pulls.get({ // needs GitHub credential
    owner: "acme-corp",
    repo: "backend",
    pull_number: 42,
  });

  // Step 2: Analyze and decide
  const review = await llm.analyze(pr.data);

  // Step 3: Post review comment
  await github.pulls.createReview({ // needs GitHub write scope
    owner: "acme-corp",
    repo: "backend",
    pull_number: 42,
    body: review.summary,
    event: "COMMENT",
  });

  // Step 4: Notify the team
  await slack.chat.postMessage({ // needs Slack credential
    channel: "#code-review",
    text: `Review posted for PR #42: ${review.summary}`,
  });

  // Step 5: Update the ticket
  await linear.issueUpdate({ // needs Linear credential
    id: "LIN-123",
    stateId: "in-review",
  });
}
Without credentials, the agent produces text. With credentials, it becomes an autonomous actor.

The delegation chain

Every agent action is a chain of trust:
User                    Agent                         API
 |                        |                            |
 | "summarize my PRs"    |                            |
 |----------------------->|                            |
 |                        | GET /repos/.../pulls      |
 |                        |--------------------------->|
 |                        | Authorization: Bearer ...  |
 |                        |                            |
 |                        | 200 OK                     |
 |                        |<---------------------------|
The credential must encode delegation: “this user authorized this agent to act on their behalf with specific permissions for a limited time.”
// Example delegated credential claims
{
  sub: "agent:pr-reviewer-v2",          // WHO is acting
  act: { sub: "user:[email protected]" },  // ON BEHALF OF whom
  scope: "repo:read repo:write",        // WITH what permissions
  exp: 1709510400,                       // UNTIL when
  aud: "https://api.github.com"         // FOR which API
}
The user is not directly performing the API call. The agent is not acting on independent authority. It is delegated authority, made verifiable through credentials.

The identity gap

Most identity systems model humans and service accounts. Agents fit neither model.
PrincipalAuthenticationDecision modelTypical scope
Human userInteractive login (SSO, MFA)Human makes decisionsBroad user permissions
Service accountStatic secret or keyFixed programmatic logicPre-provisioned system scope
AgentNeeds delegated credentialsAutonomous, LLM-drivenPer-user, per-task, time-limited
Agents act for specific users, make dynamic decisions, and need tightly scoped, short-lived access. That combination does not map cleanly to existing identity primitives.
There is no standard identity type for “an AI acting on behalf of Alice, read-only GitHub access, valid for 10 minutes.” That is the gap where most security issues begin.