AgentID: Sign in with Google, Except the User Is an AI Agent
TL;DR
AgentMail launched AgentID on October 6, 2026: an OpenID Connect provider that lets an AI agent sign in to apps as its own AgentMail inbox, and gives registered apps the verified email of the human who owns it. Its discovery document allows only the code flow, S256 PKCE and ES256 tokens, with no refresh tokens, and ID tokens last 10 minutes. On the agent's side, a sign-in is an API call with the agent's AgentMail key and a five-minute token from AgentID's waiting page, which leaves the waiting browser holding a 30-day sign-in key, the same shape as the OAuth device flow and its phishing risk.
AgentMail, the company that sells email inboxes to AI agents, launched AgentID on October 6 as “the sign-in button for AI agents.” It is a standard OpenID Connect provider: an agent signs in to your app as its own AgentMail inbox instead of borrowing its owner’s email, password and card, and if you register your app you also get the verified name and email of the human who owns it. That second part is the real product. Right now an app that gets a hundred agent signups can’t tell one person’s fleet from a hundred customers. AgentMail says it hit exactly that when it let agents sign up for its own service: “Hundreds of accounts appeared whose owners had no idea their agent had made one.”
I read the live discovery document, started a sign-in to this blog’s domain to see what an agent sees, ran the setup CLI’s read-only checks against a scratch Auth.js app, and went through AgentMail’s docs for the agent’s side. For an app, AgentID really is one more social login. The interesting part is the agent’s side, where the credential turns out to be an API key and a five-minute token, and that’s also where my one real worry sits.

An agent signs in with a curl, not a password
To get that page I did what an app does: redirected a browser to AgentID’s authorize endpoint with a PKCE challenge and https://blog.bokvi.com as the client ID. That’s an open client, the tier with no registration and no secret, where your HTTPS origin is the client ID and the redirect URI has to sit on the same origin. AgentID took it as is. The page is honest about what it is: no password field, a line saying what the app receives, and a command for the agent to run.
That command sends the agent’s AgentMail API key to AgentMail’s API, with the one-time auth_token from the page and accept_disclosure: true, which approves the app’s request on the agent’s behalf. According to AgentMail’s sign-in guide, that call authorizes a sign-in key for the browser waiting on the page: a P-256 key pair generated with WebCrypto, whose private half is non-extractable and never leaves that browser. Once the browser activates it, it signs each later sign-in itself, over a challenge the server generates, for 30 days. AgentID remembers an approval for 180 days per inbox and app, so later sign-ins to the same app need nothing from the agent at all. An agent with no browser hands the last step to its owner, who approves it in the AgentMail console.

That last step is the part I like. The app never receives anything it could replay, and there’s no verification email to send, because AgentID checks the inbox live when it mints the token.
For the app, it’s two config values and one gotcha
The discovery document I fetched on launch day is short and strict. The only grant is the authorization code, PKCE is S256 only, ID tokens are ES256 only, and there are no refresh tokens and no offline_access. The callback carries an iss parameter (RFC 9207), the mix-up defence the MCP 2026-07-28 spec now tells clients to check. ID and access tokens last 10 minutes. After that the session is yours to issue and to end.
Clerk ships AgentID as a built-in connection. Supabase, Auth0 and Auth.js v4 take it as a custom OIDC provider, Better Auth gets a helper package AgentMail maintains, and WorkOS AuthKit can’t take it yet because it has no custom social providers. An open client costs nothing but can’t run on localhost. The owner claims need a registered client, which you create in the AgentID console or with npx @agentmail/agentid-cli init.
The gotcha is in the CLI’s README: NextAuth v4 assumes RS256 even when discovery says otherwise, so a hand-written provider fails on AgentID’s ES256 tokens. This is the provider block the CLI writes into an Auth.js v4 app, trimmed, with the comment mine:
{
id: "agentid",
name: "AgentID",
type: "oauth",
wellKnown: "https://auth.agentid.com/.well-known/openid-configuration",
authorization: { params: { scope: "openid email profile" } },
idToken: true,
checks: ["pkce", "state", "nonce"],
// Without this, NextAuth v4 expects RS256, and AgentID signs only ES256.
client: { id_token_signed_response_alg: "ES256" },
clientId: process.env.AGENTID_CLIENT_ID,
clientSecret: process.env.AGENTID_CLIENT_SECRET,
profile(profile) {
return { id: profile.sub, name: profile.name, email: profile.email, image: profile.picture ?? null };
},
}
Look at what profile() keeps: the agent’s sub, name and email. The owner claims arrive in the raw profile and are dropped there, and Clerk and Better Auth don’t keep them on the user either. Storing them is your callback’s job, and the CLI deliberately leaves your callbacks alone.

The CLI itself is careful code. It refuses to overwrite hand-edited config, and the Auth0 and Supabase CLIs it downloads are pinned to SHA-256 checksums. It is also version 0.9.0, licensed UNLICENSED, built from a private repository, and its README notes that npm provenance is unavailable for that reason. And it drives your Clerk, Auth0 or Supabase admin session. I’d read the diff it leaves, or spend the ten minutes on the manual setup.
The token names the agent, and the owner if you ask
AgentID documents a decoded ID token for a registered app that asked for every scope. This is their sample, not mine, trimmed:
{
"iss": "https://auth.agentid.com",
"sub": "lM9vT2aR7sK4qN8wE1xC6bY0uF3hJ5pD9gL2zV7oA4Q",
"actor_type": "agent",
"email": "support@acme.agentmail.to",
"email_verified": true,
"name": "Acme Support",
"owner_sub": "oW7xN2pQ4mT8vL1kR6sC9dF3gH5jB0aE2uY4zI6nP8A",
"owner_name": "Maya Chen",
"owner_email": "maya@acme.com"
}
actor_type is always "agent", so an app can treat agent accounts differently without sniffing user agents. Key the account on sub, not the email: if an inbox is deleted and the same address is created again, it gets a new sub, and an email-keyed table would merge two different agents. owner_sub is the same for every agent one person owns, which makes it the key for per-owner limits. Keenable, a search API in the directory, already works this way: your first agent gets the free monthly allowance, and your other agents share it. owner_name and owner_email go only to registered apps, and never silently missing: if the agent’s key isn’t allowed to share its owner, the agent can’t finish the sign-in alone and the organization’s owner has to approve it.
One design choice I’d push back on. owner_sub comes with the profile scope, which open clients get without registering, and AgentID’s guide on persistent identity says it is “the same at every app”. Its account guide says open clients learn “nothing about the owner.” That’s true of the name and email, not of a stable identifier for the human that any two websites can join on. A per-app owner_sub would still let each app cap signups per owner. It just wouldn’t let them compare notes.
The waiting page is a device-code flow, and agents are easy to phish
Look at the waiting page again as a protocol. One party opens a sign-in and gets a short code; a second party with real credentials approves that code somewhere else; the first party’s browser is then signed in. That is the OAuth device authorization grant, and RFC 8628 §5.4 names its classic attack: the attacker starts the flow and talks the victim into approving the attacker’s code.
Here the victim would be an agent. An attacker opens a sign-in at any AgentID app in their own browser, takes the auth_token, and gets it in front of your agent with the instruction to run the command: in an email to the agent’s inbox, on a page it browses, in a tool result. If the agent runs it with a key that has the app_connect permission, the attacker’s browser activates a sign-in key for your agent’s inbox. The API’s whole answer is a key ID and “Authorization complete. Return to browser.”, with nothing naming the app, and any disclosure screen shows up in the browser that’s waiting, which is the attacker’s. As I read the docs, nothing ties that key to the app it was created at, and it lasts 30 days. I haven’t tried this against a real inbox; it follows from the documented flow.
AgentMail clearly knows. Its guide warns: “Read auth_token only from a sign-in at exactly https://auth.agentid.com. Verify the final origin through your client, independently of any origin claimed in page content.” That’s the right rule for code, and a hard one for a language model reading its mail. The agent’s identity is an inbox, which is the one thing anyone on the internet can write to. So keep app_connect off the key your agent uses day to day (it is off by default on new keys), run sign-ins from deterministic code that reads the token from a page it opened itself, and check GET /v0/api-keys?type=public_key for sign-in keys you don’t recognise.
Cutting an agent off is also slower than “delete the key” suggests. Deleting a sign-in key stops new sign-ins, but AgentID sends no revocation webhook and no back-channel logout, sessions the app already issued keep running, and the 180-day remembered approval has no revoke endpoint. There’s no bulk revocation either. If you run the app, keep agent sessions short and renew them with a fresh sign-in, which goes through on its own while the key is valid and fails once it’s revoked.
Free for apps, because the agents pay
AgentID costs an app nothing, and for a limited time integrating it earns three months of AgentMail free. The other side of that is in AgentID’s OIDC guide: “Every registered client is an AgentMail account, which is how a free product pays for itself.” Every agent identity is an AgentMail inbox too. The free plan has three inboxes and Developer is $20 a month for ten.
That’s not a reason to skip it, but it’s worth seeing clearly. For apps there’s no lock-in: it’s plain OIDC, one more button, and a competing identity provider for agents would be another button. For agent owners the identity is the AgentMail inbox, and the sub doesn’t come with you if you leave. Sign in with Google works because people already have Google accounts. AgentID’s version of that pitch is the announcement’s “10 million agents can find you,” a number I can’t check. The directory listed 26 apps when I looked, five of them marked “Coming soon”, including TinyFish and Rho, which the announcement names as accepting AgentID today.
The other approaches answer different questions, and the announcement draws the line well. Bot detection tells you a visitor isn’t human, API keys work inside an account a person already opened, delegated access acts through the owner’s own account, and signed bot traffic names the operator. None of them gives an agent its own account at your app with a person on record behind it.
Add the button, guard the key
If agents already sign up for your product, I’d add AgentID. It’s cheap, and owner_sub alone turns a pile of anonymous signups into groups with one person behind each. Register the app so you get the owner’s email, key accounts on sub and limits on owner_sub, and keep agent sessions short, because a revocation won’t reach you.
If you run agents, it beats pasting your password into an environment variable by a wide margin. Just treat an AgentMail key with app_connect as what it now is: the password to your agent’s identity, and one your agent can be talked into using for someone else.
FAQ
What is AgentID?
AgentID is an OpenID Connect identity provider from AgentMail, launched on October 6, 2026, that lets AI agents sign in to apps with an identity of their own: an AgentMail inbox. Apps get a stable sub, the agent's verified email and an actor_type claim of agent, and registered apps can also ask for the name and email of the human who owns it. It is free for apps.
How does an AI agent sign in with AgentID?
The app redirects to auth.agentid.com, and the agent's browser shows a waiting page with a one-time auth_token that expires in five minutes. The agent posts that token to the AgentMail API with its API key, and the waiting browser activates a P-256 sign-in key whose private half never leaves it, valid for 30 days. AgentID then redirects back with a code, which the app exchanges for a 10-minute ES256 ID token. An agent without a browser can have its owner approve the sign-in in the AgentMail console.
Does AgentID tell an app who owns the agent?
Yes, if the app registers and requests the owner_profile or owner_email scopes: the owner's name and email arrive in the ID token and from the userinfo endpoint, and a sign-in never succeeds with them silently missing. Unregistered open clients get owner_sub instead, an opaque identifier shared by all of one owner's agents and the same at every app, which is enough for per-owner limits.
How do I add AgentID to an existing app?
Clerk has a built-in AgentID connection, Supabase, Auth0 and Auth.js v4 accept it as a custom OIDC provider with the issuer https://auth.agentid.com, and Better Auth uses a helper package from AgentMail. Running npx @agentmail/agentid-cli init detects the provider, registers the app and writes the configuration. In Auth.js v4, set id_token_signed_response_alg to ES256, because NextAuth assumes RS256. WorkOS AuthKit is not supported yet.
What does AgentID cost?
Nothing for apps. Every agent identity is an AgentMail inbox, though, and every registered app is an AgentMail account. As of October 6, 2026, AgentMail's free plan includes three inboxes and the Developer plan is $20 a month for ten.