“Token invalid” or Verification Fails
IfverifyGrantToken() or tokens.verify() returns invalid results, work through this checklist:
Check token expiry
Decode the JWT and inspect the
exp claim. If exp is in the past, the token has expired.Check revocation status
Call
POST /v1/tokens/verify with the token. If valid: false, the token or its grant has been revoked.Verify the JWKS URI
Make sure you’re using the correct JWKS endpoint for your environment:
- Production:
https://grantex-auth-dd4mtrt2gq-uc.a.run.app/.well-known/jwks.json - Self-hosted:
https://your-domain/.well-known/jwks.json
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.”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 offline verification instead of
POST /v1/tokens/verify— the JWKS 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 offline verification instead of
| Endpoint | Limit |
|---|---|
| Global (all endpoints) | 100 req/min |
POST /v1/authorize | 10 req/min |
POST /v1/token | 20 req/min |
POST /v1/token/refresh | 20 req/min |
GET /.well-known/jwks.json | Exempt |
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:
| Error | Cause | Fix |
|---|---|---|
| ”Scopes must be a subset” | Sub-agent scopes exceed parent grant scopes | Narrow the requested scopes |
| ”Delegation depth exceeded” | Too many levels of delegation | Reduce the chain or increase the limit |
| ”Parent grant expired” | The parent grant token has expired | Obtain a new grant token first |
| ”Parent grant revoked” | The parent grant was revoked | Re-authorize the parent agent |
| ”Invalid parent grant token” | JWT signature verification failed | Check the token is well-formed |
Refresh Token Rejected
IfPOST /v1/token/refresh returns 400:
| Error message | Cause | Fix |
|---|---|---|
| ”Refresh token already used” | Token was already used (single-use rotation) | Use the new refresh token from the previous refresh response |
| ”Refresh token expired” | Token’s 30-day TTL has elapsed | Re-authorize to get a fresh token pair |
| ”Grant has been revoked” | The underlying grant was revoked | Re-authorize the agent |
| ”Agent mismatch” | agentId doesn’t match the grant’s agent | Use the correct agent ID |
| ”Invalid refresh token” | Token ID not found | Check you’re passing the correct refresh token value |
Error Codes Reference
All Grantex API errors include acode field. SDKs expose this as error.code:
| Code | HTTP Status | Description |
|---|---|---|
BAD_REQUEST | 400 | Invalid input, missing fields, or validation failure |
UNAUTHORIZED | 401 | Invalid or missing API key |
FORBIDDEN | 403 | Valid API key but insufficient permissions |
NOT_FOUND | 404 | Resource does not exist |
CONFLICT | 409 | Resource already exists (e.g., duplicate agent name) |
GONE | 410 | Resource was deleted |
VALIDATION_ERROR | 422 | Schema validation failure |
PLAN_LIMIT_EXCEEDED | 402 | Resource count exceeds plan threshold |
POLICY_DENIED | 403 | An access policy blocked the operation |
SSO_ERROR | 400 | SSO/OIDC configuration or callback error |
SERVICE_UNAVAILABLE | 503 | Service temporarily unavailable |
INTERNAL_ERROR | 500 | Unexpected server error |
Inspecting JWT Claims Locally
You can decode a Grantex grant token without verification to inspect its claims:| Claim | Description |
|---|---|
iss | Issuer (Grantex auth service URL) |
sub | Principal ID (the user who authorized) |
agt | Agent DID |
dev | Developer ID |
scp | Scopes array |
grnt | Grant record ID |
jti | Token ID (unique per token) |
iat | Issued at (Unix timestamp) |
exp | Expires at (Unix timestamp) |
aud | Audience (optional) |
parentAgt | Parent agent DID (delegation only) |
parentGrnt | Parent grant ID (delegation only) |
delegationDepth | Delegation depth (delegation only) |
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