“Token invalid” or Verification Fails
IfverifyGrantToken() or tokens.verify() returns invalid results, work through this checklist:
1
Check token expiry
Decode the JWT and inspect the
exp claim. If exp is in the past, the token has expired.2
Check revocation status
Call
POST /v1/tokens/verify with the token. If valid: false, the token or its grant has been revoked.3
Verify the JWKS URI
Make sure you’re using the correct JWKS endpoint for your environment:
- Production:
https://api.grantex.dev/.well-known/jwks.json - Self-hosted:
https://your-domain/.well-known/jwks.json
4
Check clock skew
JWT verification compares
exp and iat against your server’s clock. If your server clock is more than a few seconds off, tokens may fail verification. Use NTP to keep clocks synchronized.5
Check audience mismatch
If you set
audience in VerifyGrantTokenOptions, the JWT’s aud claim must match. Remove the audience check or ensure the authorization request included the same audience.”Authorization Stuck” — Consent Flow Not Completing
If the authorization request stays inpending status:
- Check the consent URL — ensure the user was redirected to
consentUrlfrom the authorization response - Check the auth request status — query
GET /v1/consent/:idto see the current state - Check expiry — auth requests expire after the
expiresInwindow (default 24h). If expired, create a new authorization request - Check the redirect URI — if provided, the code is delivered via redirect. Ensure your callback handler is working
- Sandbox mode — in sandbox mode, consent is auto-approved and a code is returned directly in the response
Rate Limited (429)
When you receive a429 Too Many Requests response:
- Read the
Retry-Afterheader for the minimum wait time - Implement exponential backoff with jitter (see Rate Limits guide)
- Reduce request frequency:
- Use local JWKS-based verification instead of
POST /v1/tokens/verifywhen a revocation lookup is not required. The standalone helpers still fetch JWKS per call, but that endpoint is exempt from rate limits. - Use webhooks instead of polling for grant status changes
- Cache tokens — don’t re-exchange or re-verify tokens unnecessarily
- Use local JWKS-based verification instead of
A route-specific Fastify limit replaces the 5,000 req/min Fastify default on that route. The Redis-backed standard developer plan remains an additional policy wherever standard API-key authentication applies.
Standard developer API-key plan exhaustion returns
429 with RATE_LIMIT_EXCEEDED. If the Redis-backed plan counter is unavailable, the request fails closed with 503 and RATE_LIMIT_UNAVAILABLE. Commerce, the SCIM Bearer data-plane routes under /scim/v2/*, admin, and other custom-auth routes do not consume this plan bucket; the standard API-key-authenticated /v1/scim/tokens management routes do.
Plan Limit Exceeded (402)
A402 Payment Required response means you’ve hit a resource limit for your current plan:
- Upgrade your plan — use the billing portal to switch to Pro or Enterprise
- Clean up unused resources — delete inactive agents, revoke expired grants
- Check your current usage via the developer dashboard
Delegation Failed
IfPOST /v1/grants/delegate returns an error:
Refresh Token Rejected
IfPOST /v1/token/refresh returns 400:
Error Codes Reference
All Grantex API errors include acode field. SDKs expose this as error.code:
Inspecting JWT Claims Locally
You can decode a Grantex grant token without verification to inspect its claims:Getting Help
If none of the above resolves your issue:- Check the GitHub Issues for known problems
- Open a new issue with your error message,
requestId, and reproduction steps - For Enterprise support, use the contact form