Overview
The tokens client provides four operations for managing grant tokens:
- Exchange an authorization code for a grant token
- Refresh a grant token using a refresh token
- Verify a grant token online via the Grantex API
- Revoke a grant token by its token ID (JTI)
Access the tokens client via client.tokens.
Exchange
Exchange an authorization code for a grant token after the user approves the consent request.
from grantex import Grantex, ExchangeTokenParams
with Grantex(api_key="gx_live_...") as client:
result = client.tokens.exchange(ExchangeTokenParams(
code="auth_code_from_callback",
agent_id="agt_abc123",
))
print(f"Grant token: {result.grant_token}")
print(f"Grant ID: {result.grant_id}")
print(f"Scopes: {result.scopes}")
print(f"Expires at: {result.expires_at}")
print(f"Refresh token: {result.refresh_token}")
ExchangeTokenParams
| Field | Type | Required | Description |
|---|
code | str | Yes | The authorization code received at the redirect URI. |
agent_id | str | Yes | The agent ID that initiated the authorization. |
code_verifier | str | None | No | The PKCE code verifier, if PKCE was used during authorization. |
ExchangeTokenResponse
| Field | Type | Description |
|---|
grant_token | str | The JWT grant token. |
grant_id | str | The unique grant identifier. |
scopes | tuple[str, ...] | The approved scopes. |
expires_at | str | ISO 8601 timestamp when the token expires. |
refresh_token | str | A refresh token for obtaining new grant tokens. |
Exchange with PKCE
from grantex import Grantex, ExchangeTokenParams
with Grantex(api_key="gx_live_...") as client:
result = client.tokens.exchange(ExchangeTokenParams(
code="auth_code_from_callback",
agent_id="agt_abc123",
code_verifier="dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
))
Refresh
Refresh a grant token using a refresh token. Returns a new grant token and a new refresh token (the old refresh token is invalidated). The grant_id stays the same.
Refresh tokens are single-use and rotated on every refresh per SPEC §7.4.
from grantex import Grantex, RefreshTokenParams
with Grantex(api_key="gx_live_...") as client:
new_token = client.tokens.refresh(RefreshTokenParams(
refresh_token="rt_01HXYZ...",
agent_id="agt_abc123",
))
print(f"New grant token: {new_token.grant_token}")
print(f"New refresh token: {new_token.refresh_token}")
print(f"Same grant ID: {new_token.grant_id}")
RefreshTokenParams
| Field | Type | Required | Description |
|---|
refresh_token | str | Yes | The refresh token from a previous exchange() or refresh() response. |
agent_id | str | Yes | The agent ID associated with the grant. |
Response
Returns an ExchangeTokenResponse — same shape as exchange(). See above for field descriptions.
Each refresh token can only be used once. If you attempt to reuse a refresh token, the request will be rejected with a 400 error. Always store and use the new refresh_token from the response.
Verify
Verify a grant token online via the Grantex API. This sends the token to the server for validation and returns the token’s metadata.
from grantex import Grantex
with Grantex(api_key="gx_live_...") as client:
result = client.tokens.verify("eyJhbGciOiJSUzI1NiIs...")
if result.valid:
print(f"Grant ID: {result.grant_id}")
print(f"Scopes: {result.scopes}")
print(f"Principal: {result.principal}")
print(f"Agent: {result.agent}")
print(f"Expires at: {result.expires_at}")
else:
print("Token is invalid or expired")
VerifyTokenResponse
| Field | Type | Description |
|---|
valid | bool | Whether the token is valid. |
grant_id | str | None | The grant identifier (if valid). |
scopes | tuple[str, ...] | None | The granted scopes (if valid). |
principal | str | None | The user/principal ID (if valid). |
agent | str | None | The agent DID (if valid). |
expires_at | str | None | ISO 8601 expiration timestamp (if valid). |
For offline verification that does not require a network call to the Grantex API, use the standalone verify_grant_token() function.
Revoke
Revoke a grant token by its token ID (JTI claim). The token is immediately invalidated and can no longer be used.
from grantex import Grantex
with Grantex(api_key="gx_live_...") as client:
client.tokens.revoke("tok_abc123")
# Returns None on success
Parameters
| Parameter | Type | Description |
|---|
token_id | str | The JTI (token ID) of the grant token to revoke. |
The method returns None. A GrantexApiError is raised if the token does not exist or has already been revoked.
Complete Flow Example
from grantex import (
Grantex,
AuthorizeParams,
ExchangeTokenParams,
generate_pkce,
)
pkce = generate_pkce()
with Grantex(api_key="gx_live_...") as client:
# 1. Start authorization with PKCE
auth = client.authorize(AuthorizeParams(
agent_id="agt_abc123",
user_id="user_xyz",
scopes=["files:read"],
redirect_uri="https://myapp.com/callback",
code_challenge=pkce.code_challenge,
code_challenge_method=pkce.code_challenge_method,
))
print(f"Redirect user to: {auth.consent_url}")
# 2. After user approves, exchange the code
code = "received_from_redirect" # from your callback handler
token_response = client.tokens.exchange(ExchangeTokenParams(
code=code,
agent_id="agt_abc123",
code_verifier=pkce.code_verifier,
))
# 3. Use the grant token with your agent
grant_token = token_response.grant_token
# 4. Verify the token is still valid
verification = client.tokens.verify(grant_token)
print(f"Valid: {verification.valid}")
# 5. Revoke when no longer needed
# Extract token ID from the JWT or use the known ID
client.tokens.revoke("tok_abc123")