+
+
+
+
+
+
+
+
Blog/Guides

AgentID with Clerk: What Breaks and Why

BPBinoy Perera

What goes wrong after adding AgentID to Clerk as a custom OIDC connection, and why: owner claims that are not on the Clerk user and must be fetched from userinfo, what to store on your user record, disabled connections, redirect URI mismatches, and the strategy name for custom buttons.

Guide
Guides
AgentID
clerk
troubleshooting
TL;DR

Clerk accepts agent sign-in as a custom connection, and the first sign-in usually works. What trips teams up comes later: the owner's details are not on the Clerk user, the redirect address stops matching, or the connection was never switched on. This post explains each of those, and what your app should keep on its own user record so the account survives them.

You add AI agent sign-in to Clerk by adding AgentID, the sign-in for AI agents from AgentMail, as a custom OpenID Connect (OIDC) connection that reads the issuer's discovery document, with PKCE on. After that, the part Clerk does not do for you is the owner: your app fetches the owner's claims from AgentID in its callback and stores them itself.

That split surprises people, because everything before it feels automatic. Clerk discovers the endpoints, runs the sign-in and creates a user. The agent's identity arrives, but the details of the human behind it don't show up on the Clerk user, and nothing warns you. The same goes for a few quieter failures: a connection that is configured but disabled, and a redirect address that matched in development and not in production. None of this is hard once you know where to look, and this post is where to look.

What is left to do after the Clerk guide?

After the Clerk guide, two things are left: storing the owner, and deciding which identifier your own records trust. The guide is the setup reference and ends at a working sign-in; the table below maps what people run into after that.

SymptomCauseFix
The AgentID button does nothing, or the connection is missing from sign-inNew Clerk connections start disabledEnable the connection in Clerk
AgentID rejects the redirectThe redirect URI on the AgentID application doesn't match Clerk's authorized redirect URICopy Clerk's value into the application's settings; add one per environment
Owner's name and email are not on the Clerk userClerk keeps the standard claims; the owner claims are fetched separatelyCall /v0/userinfo in your callback and store the result
The sign-in waits instead of finishingOwner scopes were requested and the agent's key can't share its ownerNothing on your side; the organization owner approves it
Your own sign-in button doesn't start the flowThe button calls a different strategy nameUse oauth_custom_agentid, the strategy name the Clerk guide uses

Two rows in that table are not failures. A sign-in that waits for the organization owner is working as designed, because the app asked for owner data the agent isn't allowed to share alone, and the owner is being asked instead. A disabled connection is simply Clerk's default for anything new. Everything else is a mismatch between two settings that should agree.

If you haven't placed the button yet, adding a Sign in with AgentID button covers the design side, including the label to use.

Why are the owner claims not on the Clerk user?

The owner claims are not on the Clerk user because they are not part of what Clerk carries onto its user, even though AgentID delivers them: a registered application that requested the owner scopes receives owner_name and owner_email in the id_token and from /v0/userinfo. On Clerk, the dependable way to get them is to call /v0/userinfo from your callback with the access token and store what comes back.

Timing matters. The access token lasts 10 minutes and there is no refresh token, so the callback is the moment to fetch, not a background job the next day. The call also re-checks live state on every request, so a revoked key or inbox shows up there rather than being hidden in a stale token.

Request only what you need. owner_sub comes with the profile scope and is enough for per-owner limits. The owner's name and email need the owner scopes, and if the agent's AgentMail API key lacks the Provider: Share Owner permission, the agent can't finish the sign-in alone; the organization owner approves it from their AgentMail account. Your app never receives a completed sign-in with the requested owner claim quietly missing.

What should you store on the user record?

Store the agent's sub as the key, owner_sub for anything per owner, and owner_email only as a way to reach a person. Your user record is the source of truth for your product, so it should hold the identifiers that don't change underneath it.

// Illustrative. What to keep on your user record after an AgentID sign-in.
type AgentAccount = {
    agentSub: string // AgentID `sub`: the account key, stable across apps
    ownerSub?: string // with the profile scope: shared by every agent one owner runs
    ownerEmail?: string // registered apps with the owner_email scope: a contact, not a key
    agentEmail: string // the agent's own inbox, which your app can write to
}

owner_email alone makes a poor key for three reasons. It only reaches registered applications that asked for it, so part of your agent traffic would have no key at all. It is a real person's address, so every table it keys becomes personal data. And one owner can run many agents, so it can't identify an account on its own.

The agent's own email belongs on the record too, for a different reason: your app can write to it. Onboarding, receipts, invoices and support replies all reach the agent directly, and it can read and answer them.

sub has the opposite properties. It is an opaque 43-character string, stable for the lifetime of the agent's inbox and the same at every app. The one event that changes it is deleting the inbox and recreating the same address, which produces a new sub, and your records should treat that as a new agent rather than merge it by email.

Why does the redirect URI keep failing?

The redirect URI fails when the address on the AgentID application and the address Clerk sends stop matching exactly. One common cause is the example subdomain left in place during setup. Another is a second Clerk instance, such as production, with an address the application doesn't list yet.

A registered AgentID application accepts up to 10 redirect URIs, so development, staging and production can each have their own entry instead of being swapped back and forth. Changing a redirect URI has one side effect worth planning for: agents who approved your app are asked to approve again, because the remembered approval is tied to the redirect URI and scopes.

The directory lists seven apps accepting AgentID today, Turso among them, and the AgentID docs hold the endpoint and claim reference. On Supabase instead, AgentID with Supabase Auth covers the same questions there, and the AgentID page on AgentMail covers the agent's side.

AgentID gives your agent a verified identity and its own email address. Free for apps to add.

FAQ

Let your agent sign in. Give it an AgentID and its own email address.