JWT vs PAT: When to Use Each
Use this guide when you need a clear answer to: "Should this call use JWT or PAT?"
Quick answer
| If your workflow is... | Use | Why |
|---|---|---|
| Interactive control-plane work (dashboard, admin/operator scripts) | JWT | JWT is the auth model for /v2/handles/*, /v2/account/domains/*, /v2/billing/*, and admin/operator surfaces |
| Server-to-server automation on one handle | PAT | PAT is handle-scoped, scope-limited, and built for automation on PAT-capable routes |
| PAT lifecycle management (create/list/revoke tokens) | JWT | PAT management endpoints themselves require signed-in handle access (OPERATOR+) |
JWT vs PAT comparison
| Dimension | JWT | PAT |
|---|---|---|
| Identity | Signed-in user session | Handle-scoped API credential |
| Typical header | Authorization: Bearer <jwt> | Authorization: Bearer patv1_<tokenId>.<secret> or x-api-key |
| Scope model | Role-based (CREATOR, OPERATOR, OWNER, platform admin) | Explicit scopes (links.read, webhooks.write, etc.) |
| Primary route family | Control plane (/v2/handles/*, /v2/account/domains/*, /v2/billing/*) | PAT-capable automation surfaces (primarily /v2/public/handles/{handle}/*) |
| Reach | All handles user can access | One handle per token |
| Session lifecycle | Login/refresh/logout flow | Create once, rotate/revoke intentionally |
Route boundary rules
Note
- Use
JWTfor control-plane and account/admin operations. - Use
PATfor automation routes that explicitly support PAT scopes. - A PAT is not a universal replacement for JWT.
JWT-first examples
POST /v2/handles/{handle}/tokens(create PAT)PUT /v2/handles/{handle}/collaboratorsPOST /v2/billing/checkout-sessionPOST /v2/account/domains
PAT-first examples
GET /v2/public/handles/{handle}/linksPUT /v2/public/handles/{handle}/links/{slug}GET /v2/public/handles/{handle}/analyticsPOST /v2/public/handles/{handle}/webhooks
Important exceptions
Most PAT usage is on /v2/public/handles/{handle}/*, but selected handle-scoped agent/QA endpoints also allow PAT when documented with PAT scopes. Treat PAT support as endpoint-specific, not automatic.
Use API Endpoint Coverage Map to confirm auth mode for a specific route.
How JWT session refresh works
JWT login returns token payload fields and sets an HttpOnly refresh cookie. Refresh uses that cookie via POST /v2/auth/refresh.
Implementation detail: the refresh cookie key is _10x_admin_refresh and the session cookie is managed as HttpOnly; Secure; SameSite=Lax.
Browser pattern
const refreshed = await fetch("https://api.10x.in/v2/auth/refresh", {
method: "POST",
headers: { "content-type": "application/json" },
credentials: "include"
});
cURL pattern (cookie jar)
# 1) Login and save cookies (includes refresh cookie)
curl -sS -c cookies.txt -X POST "https://api.10x.in/v2/auth/login" \
-H "content-type: application/json" \
-d '{"email":"owner@example.com","password":"<password>"}'
# 2) Refresh using saved refresh cookie
curl -sS -b cookies.txt -c cookies.txt -X POST "https://api.10x.in/v2/auth/refresh" \
-H "content-type: application/json"
If the refresh cookie is missing or invalid, expect 401 refresh_token_missing or 401 invalid_refresh_token.
How PAT lifecycle works
PATs are created via JWT-authenticated control-plane routes, then used for automation calls.
Implementation detail: when both x-api-key and Authorization are present, PAT parsing prioritizes x-api-key.
Bootstrap PAT with JWT
curl -sS -X POST "https://api.10x.in/v2/handles/${HANDLE}/tokens" \
-H "authorization: Bearer ${JWT_TOKEN}" \
-H "content-type: application/json" \
-d '{
"name": "ci-links",
"scopes": ["links.read","links.write","analytics.read"],
"expiresInDays": 30
}'
Use PAT in automation
curl -sS "https://api.10x.in/v2/public/handles/${HANDLE}/links" \
-H "x-api-key: ${PAT_TOKEN}"
PAT auth guardrails are rate-limited per minute both by (handle + tokenId + source IP) and by handle burst limits.
Error behavior when token type is wrong
| Situation | Typical outcome | Meaning |
|---|---|---|
| JWT expired | 401 token_expired | Re-authenticate / refresh session |
| PAT expired | 401 token_expired | Rotate or recreate PAT |
| PAT revoked | 401 token_revoked | Token was invalidated |
| JWT sent to PAT-only endpoint | 401 invalid_token_format or auth failure | Endpoint expects PAT format/scope flow |
| PAT sent to JWT-only endpoint | 403 insufficient_scope or JWT-required auth error | Endpoint requires signed-in user session |
| PAT missing required scope | 403 insufficient_scope | Token exists but does not grant needed permission |
Recommended usage patterns
Use JWT to bootstrap control-plane setup
Sign in, create/update handle resources, and mint scoped PATs.
Use PAT for unattended jobs
Run CI/CD, scheduled tasks, webhook workers, and backend integrations with least-privilege scopes.
Rotate PATs deliberately
Use one PAT per system, rotate regularly, and revoke old tokens immediately after cutover.
Do not put JWTs in long-running automation
JWTs are session-oriented and expire; use PATs for durable service credentials.
Copy-paste decision checklist
- Does this endpoint live under control-plane families (
/v2/handles/*,/v2/account/domains/*,/v2/billing/*, admin/operator)? Use JWT. - Is this an automation endpoint that explicitly supports PAT scopes? Use PAT.
- Need cross-handle user context? Use JWT.
- Need one handle + least privilege + unattended execution? Use PAT.
- Not sure? Check API Endpoint Coverage Map first.