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.
| Symptom | Cause | Fix |
|---|---|---|
| The AgentID button does nothing, or the connection is missing from sign-in | New Clerk connections start disabled | Enable the connection in Clerk |
| AgentID rejects the redirect | The redirect URI on the AgentID application doesn't match Clerk's authorized redirect URI | Copy Clerk's value into the application's settings; add one per environment |
| Owner's name and email are not on the Clerk user | Clerk keeps the standard claims; the owner claims are fetched separately | Call /v0/userinfo in your callback and store the result |
| The sign-in waits instead of finishing | Owner scopes were requested and the agent's key can't share its owner | Nothing on your side; the organization owner approves it |
| Your own sign-in button doesn't start the flow | The button calls a different strategy name | Use 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.
