How to tell agent traffic from user traffic
Your access logs probably say a person did something an agent did. Four claims fix that, and one of them exists for exactly this purpose.
Here's a question worth asking about your own system: if an agent updated a customer record an hour ago, what does your audit log say happened?
For most teams the answer is that a user did it. The agent was running on a borrowed session or a shared service account, so the log recorded the only identity it had. The action is attributed to a human who may have been asleep, and the fact that software took it is nowhere in the record.
That's a problem before it's a security problem. You can't rate-limit a population you can't see, can't answer a customer asking which changes were automated, and can't tell a noisy agent from a busy person.
The claim that exists for this
Agent access tokens issued by Agent Auth, now in early access, are JWTs signed with the same keys as your environment's user access tokens, so your existing verification path already works. What differs is what's inside.
The claim to check is sub_profile. On an agent token it's always ai_agent. On a user access token it's either absent or set to user, so the rule is to treat ai_agent as the agent case and anything else, missing claim included, as a user.
The alternative most teams reach for is pattern matching on ID prefixes, or keeping a list of known agent identifiers somewhere in the application and checking membership. Both work until they don't. A claim you can check in one comparison, that the issuer controls, doesn't drift the way a hand-maintained list does.
Actor and authority are different questions
The claim that changes how you write logs is act.sub.
An agent token carries two identities, and conflating them is the mistake the borrowed-session pattern forces on you:
subis the agent instance. This is the actor. It performed the action.act.subis the delegating user. This is the authority. The action happened because this person's permissions allowed it.
On autonomous sessions, the agent acting as itself inside an organization, act is absent. No human authorized this particular run, and the log should say so rather than inventing a person to blame.
Write both into your audit trail and the awkward questions get short answers. Which changes last quarter were made by software, and under whose authority? Did anything happen under an account after its owner was offboarded? Both are queries rather than investigations.
The agent marker goes in metadata rather than in actor.type. Target types take customer-defined values and are registered through the schema API, but the actor schema only accepts custom metadata, so metadata is where a marker is guaranteed to survive. The actor id still distinguishes the two populations on its own, since an agent instance ID is never a user ID.
One caution on declared_intent. The intent string is supplied by whoever requested the token and is never verified. It's useful context for reconstructing what a session was for, and it is not evidence of what the session did.
Seeing the fleet, not just the requests
Per-request claims tell you who is calling right now. The lifecycle events tell you what exists, through webhooks and the Events API:
agent.blueprint.created,agent.blueprint.updated,agent.blueprint.deletedagent.instance.created,agent.instance.deletedagent.instance.session.created,agent.instance.session.revoked
agent.blueprint.created is the one to route somewhere a human reads. A new blueprint is a new permission ceiling entering your environment, which is a change worth the same attention as a new role. agent.instance.created tells you an agent has started running in an organization that hadn't used it before, which is either an adoption signal or a surprise.
What to look at once you can see it
Being able to separate the two populations makes a few things measurable that weren't:
- Agent share of write traffic. A number nobody has and everybody should. It tends to move faster than teams expect.
- Requests denied by permission, split by caller type. Denials against user traffic usually mean a permissions bug. Denials against agent traffic usually mean a blueprint ceiling that doesn't match the agent's tool list, which is worth fixing at the blueprint rather than by widening a role.
- Sessions created per instance over time. A step change without a deploy is worth a look.
- Actions attributed to an agent whose
act.subno longer has a membership. Should be empty, since authority is re-derived at each refresh and delegated sessions stop refreshing when the user's session ends. If it isn't empty, something upstream is holding a token longer than it should.
Try it
Agent Auth is in early access. The docs cover the full claim set, the lifecycle events, and how sessions are scoped and revoked. If your logs currently can't tell you which of your traffic is software, get in touch and we'll enable it for your environment.