grantex
Version 0.7.2 adds typed DPDP client responses and the optional enterprise
SSO session_token field. Check Release Status
for the version currently available on PyPI; a source version is not a registry release.
Version 0.7.0: enforce() checks the grant audience,
requires an amount for capped scopes, and checks current revocation online by
default. Per-call revocation settings cannot weaken the client setting.
Python 3.9+ remains supported. These are breaking changes: follow the
enforcement migration guide.
verify_grant_token() alone does not check current revocation. Verify
publication in Release Status before installing.
Version 0.6.1: exposes the signed WebAuthn grant evidence reference and
webauthn_verified VC attestation field. An evidence reference is not the raw
assertion or a current revocation check. Verify issuer, enrollment, RP ID,
origin, digest, and current status; see the
passkey guide.
Version 0.6.0: adds hosted passkey enrollment sessions and account-level irregularity response policy methods. Both require the server's corresponding feature flags and an authenticated customer handoff for enrollment. Confirm the published version in Release Status before installing.
Server-side portable issuance requires an explicit rollout.
0.5.1: security and reliability fixes. enforce() applies the
tightest budget cap and denies malformed, negative or non-finite amounts; the
FastAPI enforcer reads the Authorization header; grant-token verification
caches the JWKS and runs off the event loop; resource ids are percent-encoded;
single-use authorization codes are never retried; and event streams bound
connect time at 10 seconds.
0.5.0: agent and principal wallet clients expose
reconcile_reservation(). Authorization dictionaries preserve the server's
additive evmPayment payload. Automatic x402 HTTP retries are currently provided
by the TypeScript adapter, not this SDK. See Base custody setup.
Python SDK for the Grantex delegated authorization protocol — OAuth 2.0 for AI agents.
Grantex lets humans authorize AI agents with verifiable, revocable, audited grants built on JWT and the OAuth 2.0 model. This SDK provides a complete client for the Grantex API.
Homepage | Docs | API Reference | Sign Up Free | GitHub
Install
pip install grantex
Quick start
from grantex import AuthorizeParams, ExchangeTokenParams, Grantex, VerifyGrantTokenOptions, verify_grant_token
client = Grantex(api_key="YOUR_API_KEY", audience="https://api.merchant.example")
# 1. Register the agent and its allowed resource, then request authorization
agent = client.agents.register(
name="Email Assistant",
description="Reads files and sends email for the principal",
scopes=["files:read", "email:send"],
resource_servers=["https://api.merchant.example"],
)
request = client.authorize(AuthorizeParams(
agent_id=agent.id,
user_id="usr_01HXYZ...",
scopes=["files:read", "email:send"],
audience="https://api.merchant.example", # must match the relying party
))
# Redirect the user to the consent page — they approve in plain language
print(request.consent_url)
# 2. Exchange the authorization code for a grant token
# (your redirect callback receives the `code` after user approves)
token = client.tokens.exchange(ExchangeTokenParams(code=code, agent_id=agent.id))
# Deliver token.grant_token securely to the agent; do not log it.
print(token.scopes) # ('files:read', 'email:send')
# 3. Verify locally using keys retrieved from the issuer's JWKS
grant = verify_grant_token(
token=token.grant_token,
options=VerifyGrantTokenOptions(
jwks_uri="https://api.grantex.dev/.well-known/jwks.json",
audience="https://api.merchant.example",
),
)
print(grant.principal_id) # 'usr_01HXYZ...'
# 4. Revoke when done
client.tokens.revoke(grant.token_id)
Local JWKS verification
Verify grant-token signatures locally using the issuer's public JWKS. The verifier fetches signing keys when needed and caches them; it does not query current grant revocation status. Configure the expected audience for audience-bound tokens:
from grantex import VerifyGrantTokenOptions, verify_grant_token
verified = verify_grant_token(
token="eyJhbGciOiJSUzI1NiIs...",
options=VerifyGrantTokenOptions(
jwks_uri="https://api.grantex.dev/.well-known/jwks.json",
audience="https://api.merchant.example",
),
)
print(verified.scopes) # ['files:read', 'email:send']
print(verified.principal_id) # 'usr_01HXYZ...'
print(verified.agent_did) # 'did:web:...'
For a new passkey-approved grant, verified.webauthn_evidence contains the
signed assertion digest reference. Pass credential_format="vc-jwt" during
exchange to request an opt-in VC containing the full assertion, then inspect
client.credentials.verify(vc_jwt).webauthn_verified. Independently trust
the issuer and enrollment, check revocation, pin the RP ID and origin, and
compare the VC evidence digest with the grant reference. Raw credential public
keys can correlate presentations.
PKCE Support
The SDK includes built-in PKCE (Proof Key for Code Exchange) support using the S256 method:
from grantex import AuthorizeParams, ExchangeTokenParams, Grantex, generate_pkce
client = Grantex(api_key="YOUR_API_KEY")
# 1. Generate a PKCE challenge
pkce = generate_pkce()
# pkce.code_verifier — random 43-char string (keep secret)
# pkce.code_challenge — SHA-256 hash of verifier (send to server)
# pkce.code_challenge_method — 'S256'
# 2. Pass the challenge when requesting authorization
request = client.authorize(AuthorizeParams(
agent_id="ag_01HXYZ...",
user_id="usr_01HXYZ...",
scopes=["files:read"],
code_challenge=pkce.code_challenge,
code_challenge_method=pkce.code_challenge_method,
))
# 3. Exchange the code with the verifier
token = client.tokens.exchange(ExchangeTokenParams(
code="auth_code_from_redirect",
agent_id="ag_01HXYZ...",
code_verifier=pkce.code_verifier,
))
Features
| Feature | Description |
|---|---|
| Authorization flow | client.authorize() — initiate consent, get grant tokens |
| Token exchange | client.tokens.exchange() — exchange an authorization code for a grant token |
| Token management | client.tokens.verify(), .revoke() — online verification and revocation |
| Local verification | verify_grant_token() — retrieves JWKS, then performs the RS256 signature check locally |
| Agent management | client.agents.register(), .get(), .list(), .update(), .delete() |
| Grant management | client.grants.list(), .get(), .revoke() |
| Multi-agent delegation | client.grants.delegate() — scoped sub-grants with cascade revocation |
| Audit trail | client.audit.log(), .list(), .get() — tamper-evident hash-chained log |
| Policy engine | client.policies.create(), .list(), .update(), .delete() |
| Anomaly detection | client.anomalies.list(), .detect() |
| Account irregularity response | 0.6.0: client.anomalies.get_response_policy(), .set_response_policy("alert_only") (server flag required) |
| Hosted passkey enrollment | 0.6.0: client.webauthn.create_enrollment_session(principal_id=...) after customer authentication (server flag required) |
| Compliance | client.compliance.get_summary(), .export_audit(), .export_grants(), .evidence_pack() |
| Webhooks | client.webhooks.create(), .list(), .delete() + verify_webhook_signature() |
| Billing | client.billing.get_subscription(), .create_checkout(), .create_portal() |
| SCIM 2.0 | client.scim.create_user(), .list_users(), .get_user(), .update_user(), .delete_user() |
| OIDC SSO | client.sso.create_config(), .get_config(), .get_login_url(), .handle_callback() |
| Agent prepaid wallets | Developer policy, principal wallet/policy/approval, and ES256 DPoP agent clients |
| Commerce V1/OACP | client.commerce.get_profile(), .search_catalog(), .create_cart(), .get_ops_health() |
Version 0.7.2 parses optional session_token from enterprise SSO callbacks
when the server enables human sessions. Treat the token
as an opaque bearer credential, not a public session ID; keep it out of logs.
Hosted dashboard sign-in currently uses OIDC and machine API keys remain
usable for automation. See the
enterprise SSO guide.
Enrollment links are one-use bearer secrets. Alert-only is account-wide and affects the legacy detector's automatic revocation, not other security gates.
Agent prepaid wallets (0.4+)
from grantex import (
AgentPrepaidWalletClient,
PrincipalPrepaidWalletClient,
generate_dpop_key,
)
principal = PrincipalPrepaidWalletClient(
base_url="https://api.grantex.dev",
session_token=session_token,
)
principal.create_spend_policy({
"name": "Research group budget",
"scopeType": "group",
"scopeId": "research-agents",
"effect": "limit",
"maxAmount": "1000000",
"windowType": "month",
"onExceed": "require_approval",
})
agent = AgentPrepaidWalletClient(
access_token=access_token,
private_key=generate_dpop_key(), # persist securely across process restarts
resource_url="https://grantex.dev/v1/prepaid-wallets",
)
result = agent.authorize_payment({
"amount": "2500",
"asset": "USDC",
"network": "grantex:prepaid",
"recipient": "merchant:data-api",
"resource": "https://merchant.example/data",
"scope": "data:read",
"merchantId": "merchant:data-api",
"purpose": "research",
"maxTimeoutSeconds": 120,
"idempotencyKey": logical_payment_id,
})
if result.get("status") == "approval_required":
principal.decide_payment_approval(
result["approvalRequestId"], "approved", "Exact request reviewed"
)
The agent client creates a fresh ES256 DPoP proof for each request and binds it to the access token, method, and exact URL. An approved retry must preserve the original wallet, idempotency key, amount, payee, resource, and semantic context. Grantex governs delegated spend; external issuer/custody, KYC, sanctions, settlement, dispute, and reconciliation controls remain operator dependencies.
Commerce V1 / OACP
profile = client.commerce.get_profile(merchant_id="mch_shopify_mgx0n6_22")
products = client.commerce.search_catalog({
"merchant_id": "mch_shopify_mgx0n6_22",
"limit": 3,
})
Accredited issuer adapters
The registry asks an accredited issuer to attest an agent through one seam,
grantex.issuers.AccreditedIssuerClient: issuer_metadata(),
request_attestation(agent_record, proved_key) and fetch_status(credential_ref).
load_issuer_client() builds the adapter named in GRANTEX_ISSUER_ADAPTER
through the grantex.issuers entry point group; the SDK registers mock, which
drives the repository's mock issuer with no network. A real issuer's adapter is
its own package, configured by GRANTEX_ISSUER_BASE_URL,
GRANTEX_ISSUER_CLIENT_ID / GRANTEX_ISSUER_CLIENT_SECRET (or
GRANTEX_ISSUER_TOKEN) and GRANTEX_ISSUER_SCOPES; a name that is not
installed fails closed. See
Implementing an Issuer Adapter.
The attestation step of registration runs as one command:
grantex-attest ag_01ABC --key agent-key.json --generate-key
It registers the key in the agent's history (client.agents.keys.add), proves
possession with the registry's challenge (sign_key_proof), hands the proved
key to the adapter, posts the attestation to the registry and prints the
agent's level. The same steps are grantex.issuers.attest_agent().
Configuration
from grantex import Grantex
# Explicit API key
client = Grantex(api_key="gx_live_...")
# Or via environment variable
# export GRANTEX_API_KEY=gx_live_...
client = Grantex()
# Custom base URL (self-hosted)
client = Grantex(
api_key="gx_live_...",
base_url="https://auth.your-company.com",
)
# Custom timeout (seconds)
client = Grantex(api_key="gx_live_...", timeout=60.0)
The client also works as a context manager:
with Grantex(api_key="gx_live_...") as client:
agents = client.agents.list()
Error handling
from grantex import Grantex, GrantexApiError, GrantexAuthError, GrantexNetworkError
client = Grantex(api_key="gx_live_...")
try:
client.agents.get("ag_invalid")
except GrantexAuthError:
# 401 — invalid or expired API key
pass
except GrantexApiError as e:
# Any other API error (4xx/5xx)
print(e.status_code, e.code, e.message)
except GrantexNetworkError:
# Connection failure, timeout, DNS error
pass
Requirements
- Python 3.9+
- httpx (sync HTTP client)
- PyJWT + cryptography (for local token signature verification)
Links
Grantex Ecosystem
| Package | Description |
|---|---|
@grantex/sdk |
TypeScript SDK |
@grantex/langchain |
LangChain integration |
@grantex/autogen |
AutoGen integration |
@grantex/vercel-ai |
Vercel AI SDK integration |
grantex-crewai |
CrewAI integration |
grantex-openai-agents |
OpenAI Agents SDK integration |
grantex-adk |
Google ADK integration |
@grantex/mcp |
MCP server for Claude Desktop / Cursor / Windsurf |
@grantex/cli |
Command-line tool |
Scope Enforcement (v0.3.1)
Enforce tool-level permissions on any connector — define your own manifests or use the 53 pre-built ones.
from grantex import Grantex, ToolManifest, Permission
grantex = Grantex(api_key="gx_...")
# Define a manifest for any connector — no dependency on Grantex to add support
grantex.load_manifest(ToolManifest(
connector="my-crm",
tools={"search": Permission.READ, "create_deal": Permission.WRITE, "delete_account": Permission.DELETE},
))
result = grantex.enforce(grant_token=token, connector="my-crm", tool="delete_account")
# result.allowed = False — "write scope does not permit delete operations"
Features:
enforce()— verify JWT + check tool permission via manifest, <1mswrap_tool()— auto-enforce on LangChain toolsGrantexEnforcer— FastAPI dependency for scope enforcement, including decision grants (Grantex-Decision-Grantheader, JSON body arguments,case_versioncallback)- Define custom manifests for any connector: inline, from JSON, or auto-generated via CLI
- 53 pre-built manifests included (Salesforce, HubSpot, Jira, Stripe, SAP, S3, and 47 more)
- Permission hierarchy:
admin > delete > write > read - Permissive mode for migration (
enforce_mode="permissive")
License
Apache 2.0
Ownership
Grantex is owned by Orchestrum Technologies LLP. Inventor and owner: Sanjeev Kumar. Ownership contact: sanjeev@orchestrum.in or mishra.sanjeev@gmail.com.
Authority hardening requires Python SDK 0.7.1 (
current_authority), not just a valid JWT signature. Bind the audience and trusted human/agent identities, and check the issuer before every execution. This does not create human consent or automatically consume action decisions/spend caps. Existing offline defaults remain offline. Verify registry availability before installation. See the SDK execution authority guide.
Metadata
Release files for grantex 0.7.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| grantex-0.7.2.tar.gz | 306.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| grantex-0.7.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 531.6 kB
Release files / grantex-0.7.2.tar.gz
| Download URL | grantex-0.7.2.tar.gz |
|---|---|
| Size | 306.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c666945f0469f59d91ca2723dc7851df15f49d5a2a5ee503ea3dd3922a6947b4
|
|
BLAKE2b-256 checksum How to use checksums |
c8292cbb817739f25375d288ad68d6b62aabd4a83dff98b97a9f1cf7e32372cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / grantex-0.7.2-py3-none-any.whl
| Download URL | grantex-0.7.2-py3-none-any.whl |
|---|---|
| Size | 225.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dec3f6fd3c28569a4a107808a5eef9a14537623e617247b4824867438701e9f5
|
|
BLAKE2b-256 checksum How to use checksums |
ddb4ac385024826d697212f5e722537350d6269e5ef624685327d685a7573f97
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|