Auth and Clerk
Configure Clerk as the authentication layer for Convoy deployments, including the JWT template and user sync flow.
Convoy uses Clerk for user authentication. The web app signs users in through Clerk; Convex validates those sessions through a JWT issuer configured to match the same Clerk instance. Authorization (who can do what inside Convoy) is separate from this and lives in the project's organization/project permission model.
Required env vars
Set these Clerk-related env vars in the places listed.
| Variable | Where | Purpose |
|---|---|---|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Web host | Browser-side Clerk client |
CLERK_SECRET_KEY | Web host | Server-side Clerk client |
NEXT_PUBLIC_CLERK_SIGN_IN_URL | Web host | Typically /auth/sign-in |
NEXT_PUBLIC_CLERK_SIGN_UP_URL | Web host | Typically /auth/sign-up |
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL | Web host | Post-sign-in, typically /auth/callback |
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL | Web host | Post-sign-up, typically /auth/callback |
CLERK_JWT_ISSUER_DOMAIN | Convex dashboard | Issuer domain Convex validates JWTs against |
See Environment Variables for
the full matrix.
How Clerk and Convex are wired
Convoy's apps/web/convex/auth.config.ts registers a single JWT
provider, keyed on the Clerk issuer domain you configure in the Convex
dashboard:
export default {
providers: [
{
domain: process.env.CLERK_JWT_ISSUER_DOMAIN!,
applicationID: "convex",
},
],
} satisfies AuthConfig;That applicationID: "convex" value is what the Clerk JWT template's
audience (aud) must be set to for Convex to accept the token.
Configure the JWT template in Clerk
- Open the Clerk dashboard for the app you want Convex to trust.
- Go to JWT Templates → New template.
- Use the built-in Convex preset if Clerk offers it. If creating
manually, set:
- Name:
convex - Signing algorithm:
RS256(default) - Lifetime / Allowed clock skew: defaults are fine
- Claims: at minimum,
"aud": "convex". Clerk's preset also populates the Clerk standard claims Convoy expects (sub,email,name,picture_url).
- Name:
- Save.
- Copy the Issuer field from Clerk (looks like
https://your-app.clerk.accounts.devin dev, or your configured custom domain in prod). That is the valueCLERK_JWT_ISSUER_DOMAINmust match in your Convex dashboard environment variables.
Allowed origins and redirect URLs
In the Clerk dashboard, under Paths / Domains and Frontend API, make sure:
- the deployed web app origin is listed as an allowed origin
- the sign-in, sign-up, and callback paths match the
NEXT_PUBLIC_*_URLvalues you set on the web host - OAuth/social provider redirect URIs on the Clerk side match the deployed host
Mismatches usually surface as "invalid session" errors immediately after a user signs in.
User sync flow (how a Clerk user becomes a Convoy user)
On sign-in, the web app calls the Convex users.store mutation. That
mutation reads ctx.auth.getUserIdentity() (which Convex populates from
the validated Clerk JWT) and upserts a row in users keyed on
tokenIdentifier:
- First call: creates a
usersrow withemail,name, andavatarUrlfrom the Clerk identity. - Subsequent calls: updates
email/name/avatarUrlif they have changed since the last sign-in.
On first onboarding, users.onboard additionally bootstraps the
user's first organization membership. A tokenIdentifier that doesn't
match any existing users row is an implicit "new user" signal — the
onboarding path handles it.
If sign-in succeeds in Clerk but Convoy never sees the user, the
Clerk JWT template or the Convex CLERK_JWT_ISSUER_DOMAIN is usually
the problem. Convex's Functions → Logs tab will show failed
users.store calls when the identity is missing.
Environment separation
Keep production and non-production Clerk instances separate. A non-production deployment that points at a production Clerk app will happily sync production users into its own Convex, which is almost never what you want.
| Environment | Clerk app | Convex deployment | CLERK_JWT_ISSUER_DOMAIN |
|---|---|---|---|
| Dev | dev Clerk app | dev:... Convex | issuer of the dev Clerk app |
| Staging | staging Clerk app | staging Convex | issuer of the staging Clerk app |
| Prod | prod Clerk app | prod:... Convex | issuer of the prod Clerk app |
Enterprise / SSO
Clerk supports SSO (SAML, OIDC), domain restriction, machine users, and allowlists — Convoy inherits whatever Clerk exposes. The setup lives in the Clerk dashboard, not in Convoy. See Clerk's own docs for the authoritative list; once configured, nothing in Convoy needs to change.
What this does not replace
Clerk answers "is this request authenticated and who is it?". It does
not answer "can this user do this?". Authorization decisions happen
inside Convex functions via Convoy's organization and project
permission helpers (convex/lib/access/permissions.ts).