@grantex/sdk
v0.8.2
Published
TypeScript SDK for the Grantex delegated authorization protocol
Downloads
19,319
Maintainers
Readme
@grantex/sdk
TypeScript SDK for the Grantex delegated authorization protocol — OAuth 2.0 for AI agents.
Homepage | Docs | API Reference | Sign Up Free | GitHub
Installation
Version 0.8.2 adds typed DPDP client responses and the optional enterprise
SSO sessionToken field. Check Release Status
for the version currently available on npm; a source version is not a registry release.
Version 0.8.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.
Node.js 22.12+ is required; Node.js 24 LTS is recommended. These are breaking
changes: follow the enforcement migration guide.
verifyGrantToken() alone remains cryptographic verification, not a current
revocation check. Verify publication in Release Status before installing.
Version 0.7.1: exposes the signed WebAuthn evidence reference on verified
grants and the webauthnVerified VC attestation response. A reference is not
the raw assertion or a current revocation check; verify the issuer, RP ID,
origin, evidence digest, and current credential status. The server controls
issuance separately with PORTABLE_WEBAUTHN_EVIDENCE_ENABLED; see the
passkey guide.
Version 0.7.0: adds hosted passkey enrollment sessions and
account-level irregularity response policy methods. The server must enable the
corresponding feature flags; this SDK does not register a passkey without an
authenticated customer handoff. Version 0.6.0 added wallet clients that preserve the
server's Base USDC evmPayment payload and expose reconcileReservation() for
finalized settlement/expiry. Use @grantex/[email protected] or later for
the automatic 402/payment/retry flow. Confirm the published SDK version in
Release Status before installing.
See Base custody setup.
npm install @grantex/sdkQuick Start
import { Grantex, verifyGrantToken } from '@grantex/sdk';
const grantex = new Grantex({ apiKey: 'YOUR_API_KEY', audience: 'https://api.merchant.example' });
// 1. Register an agent
const agent = await grantex.agents.register({
name: 'Email Assistant',
description: 'Reads and sends email on behalf of users',
scopes: ['email:read', 'email:send'],
});
// 2. Request authorization
const { consentUrl } = await grantex.authorize({
agentId: agent.agentId,
userId: 'usr_01J...',
scopes: ['email:read', 'email:send'],
resourceServers: ['https://api.merchant.example'],
audience: 'https://api.merchant.example',
});
// Redirect the user to consentUrl — they approve in plain language
// 3. Exchange authorization code for a grant token
// (your redirect callback receives the `code` after user approves)
const token = await grantex.tokens.exchange({ code, agentId: agent.agentId });
// Treat token.grantToken as a bearer credential; do not log it.
console.log(token.scopes); // ['email:read', 'email:send']
console.log(token.grantId); // 'grnt_01J...'
// 4. Verify locally using keys retrieved from the issuer's JWKS
const grant = await verifyGrantToken(token.grantToken, {
jwksUri: 'https://api.grantex.dev/.well-known/jwks.json',
audience: 'https://api.merchant.example',
});
console.log(grant.principalId); // 'usr_01J...'
// 5. Revoke when done
await grantex.tokens.revoke(grant.tokenId);Configuration
const grantex = new Grantex({
apiKey: 'gx_....', // or set GRANTEX_API_KEY env var
baseUrl: 'https://api.grantex.dev', // default
issuer: 'https://grantex.dev', // optional when token issuer differs from API host
jwksUri: 'https://api.grantex.dev/.well-known/jwks.json', // optional JWKS override
timeout: 30000, // request timeout in ms (default: 30s)
});| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | process.env.GRANTEX_API_KEY | API key for authentication |
| baseUrl | string | https://api.grantex.dev | Base URL of the Grantex API |
| issuer | string | derived from jwksUri | Expected JWT issuer for local signature verification |
| jwksUri | string | ${baseUrl}/.well-known/jwks.json | URL from which the verifier retrieves signing keys |
| timeout | number | 30000 | Request timeout in milliseconds |
OAuth Agent Profile Client
OAuthAgentClient implements the client role in the prepared
draft-mishra-oauth-agent-grants-03 profile. It discovers and validates RFC
8414 metadata, creates an ES256 DPoP key by default, uses PAR and PKCE S256,
validates state and the RFC 9207 iss response parameter, rotates refresh
tokens, performs same-resource RFC 8693 attenuation, and creates DPoP proofs
for protected-resource requests.
import { OAuthAgentClient } from '@grantex/sdk';
const client = await OAuthAgentClient.create({
issuer: 'https://grantex.dev',
clientId: 'ag_01J...',
redirectUri: 'https://agent.example/callback',
resource: 'https://api.example/resource',
});
const pending = await client.beginAuthorization({
scopes: ['grantex.resource.read'],
principalHint: '[email protected]',
});
// Redirect the Principal to pending.authorizationUrl. In the callback:
const tokens = await client.completeAuthorization(callbackUrl);
const response = await client.fetch(
'https://api.example/resource',
tokens.access_token,
);
const narrower = await client.attenuate(tokens.access_token, [
'grantex.resource.read',
]);
const refreshAttempt = crypto.randomUUID();
const rotated = await client.refresh(tokens.refresh_token!, {
idempotencyKey: refreshAttempt,
});
await client.revoke(rotated.refresh_token!, 'refresh_token');Persist the idempotency key with the old refresh token when lost-response
recovery must survive a caller restart. Repeating both within 300 seconds uses
a fresh DPoP proof and returns the exact committed token values with a
recalculated, non-extended expires_in. A
different key or DPoP identity is treated as refresh-token reuse and revokes
the token family. When no key is supplied, the client generates and retains one
for the old refresh token for five minutes in the current process.
Persist the generated key securely if an instance must survive process
restarts. Supply the matching privateKey and publicJwk to create; both are
required together. principalHint is optional account-discovery input and is
not proof of the Principal's identity; live approval still requires the
authorization server's passkey authentication. Plain HTTP endpoints are rejected unless
allowInsecureLoopback is enabled for local loopback testing. Revision -02
of the draft family is published as an active individual Internet-Draft;
revision -03 is the working candidate implemented here. Neither is an
IETF-endorsed or independently certified standard.
Agent prepaid wallets (SDK 0.5+)
PrepaidWalletAgentClient uses an OAuthAgentClient and DPoP access token to
list assigned wallets, reserve payments, and request threshold reloads.
PrincipalPrepaidWalletClient uses a short-lived principal-session token to
create and fund wallets, assign safe-default policy, manage layered spend
policies and exact payment approvals, approve reloads, inspect activity, and
block an assignment, wallet, or all wallets for one agent.
DeveloperPrepaidWalletPolicyClient manages tenant-level policy with the
developer API key.
The access token must include wallet:spend and each action scope used in a
payment (for example weather:read). Agent wallet listings intentionally omit
custody-provider IDs, wallet addresses, principal IDs, and wallet metadata.
import {
PrepaidWalletAgentClient,
PrincipalPrepaidWalletClient,
} from '@grantex/sdk';
const agentWallets = new PrepaidWalletAgentClient({
oauthClient,
accessToken,
});
const principalWallets = new PrincipalPrepaidWalletClient({
baseUrl: 'https://api.grantex.dev',
sessionToken,
});
await principalWallets.createSpendPolicy({
name: 'Shared research budget',
scopeType: 'group',
scopeId: 'research-agents',
effect: 'limit',
maxAmount: '1000000',
windowType: 'month',
onExceed: 'require_approval',
purposes: ['research'],
});
const authorization = await agentWallets.authorizePayment({
amount: '1000',
asset: 'USDC',
network: 'grantex:prepaid',
recipient: 'merchant:weather-api',
resource: 'https://merchant.example/weather',
scope: 'weather:read',
merchantId: 'merchant:weather-api',
purpose: 'research',
projectId: 'climate-2026',
costCenter: 'engineering',
maxTimeoutSeconds: 120,
idempotencyKey: crypto.randomUUID(),
});authorizePayment returns either a signed reservation or an
approval_required response. After the principal approves that exact request,
retry with its approvalRequestId, the same wallet, idempotency key, and all
original payment fields. Approval is short-lived and single-use.
Amounts are atomic-unit integer strings. Layered policy and exact approval are
available in @grantex/sdk 0.5.0 and later.
Self-hosted wallet deployments must also provide correct public resource
routing, migrations 091 and 092, durable notification delivery, merchant-side
idempotency, and any external custody/provider integration. See Prepaid Wallet
Production
Readiness;
installing the SDK alone does not provide those dependencies.
Commerce V1 / OACP
The SDK includes a commerce resource for the Grantex Commerce V1 control
plane and OACP live-pilot flow.
import { Grantex } from '@grantex/sdk';
const grantex = new Grantex({ apiKey: process.env.GRANTEX_API_KEY! });
// Public merchant publishing profile
const profile = await grantex.commerce.getProfile({
merchantId: 'mch_shopify_mgx0n6_22',
});
console.log(profile.merchant?.merchant_id);
// Catalog grounding
const catalog = await grantex.commerce.searchCatalog({
merchant_id: 'mch_shopify_mgx0n6_22',
query: 'shirt',
limit: 5,
});
// Agent cart creation. Commerce write paths require Idempotency-Key.
const cart = await grantex.commerce.createCart({
idempotencyKey: crypto.randomUUID(),
merchant_id: 'mch_shopify_mgx0n6_22',
currency: 'INR',
line_items: [
{ variant_id: String(catalog.items[0]?.['variant_id']), quantity: 1 },
],
});
// Consent request and Commerce Passport exchange
const consent = await grantex.commerce.createConsentRequest({
merchant_id: 'mch_shopify_mgx0n6_22',
passport_type: 'checkout',
max_amount: Number(cart.data['total_amount']),
currency: 'INR',
});
// Redirect the buyer to consent.data['consent_url'], then exchange after
// the consent request is granted.
const passport = await grantex.commerce.exchangeConsentForPassport({
consent_request_id: String(consent.data['consent_request_id']),
});
const payment = await grantex.commerce.createPaymentIntent({
idempotencyKey: crypto.randomUUID(),
merchant_id: 'mch_shopify_mgx0n6_22',
cart_id: String(cart.data['cart_id']),
passport_jwt: String(passport.data['passport_jwt']),
amount_minor_units: Number(cart.data['total_amount']),
currency: 'INR',
provider_key: 'plural',
});
const checkout = await grantex.commerce.createCheckoutLink(
String(payment.data['payment_intent_id']),
{
idempotencyKey: crypto.randomUUID(),
passport_jwt: String(passport.data['passport_jwt']),
success_url: 'https://buyer.example/success',
cancel_url: 'https://buyer.example/cancel',
},
);Plural webhook intake is available at
https://api.grantex.dev/v1/webhooks/providers/plural. Provider webhooks are
normally called by the provider dashboard, not by application code. The SDK
also exposes grantex.commerce.getOpsHealth() and
grantex.commerce.listProviderWebhookEvents() for operator health checks.
PKCE Support
The SDK includes built-in PKCE (Proof Key for Code Exchange) support using the S256 method for secure authorization flows:
import { Grantex, generatePkce } from '@grantex/sdk';
const grantex = new Grantex({ apiKey: 'YOUR_API_KEY' });
// 1. Generate a PKCE challenge
const pkce = generatePkce();
// pkce.codeVerifier — random 43-char string (keep secret)
// pkce.codeChallenge — SHA-256 hash of verifier (send to server)
// pkce.codeChallengeMethod — 'S256'
// 2. Pass the challenge when requesting authorization
const { consentUrl } = await grantex.authorize({
agentId: 'ag_01J...',
userId: 'usr_01J...',
scopes: ['files:read'],
codeChallenge: pkce.codeChallenge,
codeChallengeMethod: pkce.codeChallengeMethod,
});
// 3. Exchange the code with the verifier
const token = await grantex.tokens.exchange({
code: 'auth_code_from_redirect',
agentId: 'ag_01J...',
codeVerifier: pkce.codeVerifier,
});API Reference
Authorization
grantex.authorize(params)
Initiate the delegated authorization flow. Returns a consent URL to redirect the user to.
const request = await grantex.authorize({
agentId: 'ag_01J...',
userId: 'usr_01J...',
scopes: ['files:read', 'email:send'],
audience: 'https://api.example.com', // optional; becomes the JWT aud claim
expiresIn: '24h', // optional
redirectUri: 'https://...' // optional
});
console.log(request.consentUrl); // redirect user here
console.log(request.authRequestId); // track the request
console.log(request.expiresAt); // ISO 8601 timestampReturns: AuthorizationRequest
| Field | Type | Description |
|-------|------|-------------|
| authRequestId | string | Unique ID for this authorization request |
| consentUrl | string | URL to redirect the user to for consent |
| agentId | string | The agent requesting authorization |
| principalId | string | The user being asked for consent |
| scopes | string[] | Requested scopes |
| expiresAt | string | When the request expires (ISO 8601) |
| status | string | 'pending', 'approved', 'denied', or 'expired' |
Agents
grantex.agents.register(params)
Register a new AI agent.
const agent = await grantex.agents.register({
name: 'Code Review Bot',
description: 'Reviews pull requests and suggests improvements',
scopes: ['repo:read', 'pr:comment'],
});grantex.agents.get(agentId)
const agent = await grantex.agents.get('ag_01J...');grantex.agents.list()
const { agents } = await grantex.agents.list();grantex.agents.update(agentId, params)
const agent = await grantex.agents.update('ag_01J...', {
name: 'Updated Name',
scopes: ['repo:read', 'pr:comment', 'pr:approve'],
});grantex.agents.delete(agentId)
await grantex.agents.delete('ag_01J...');Grants
grantex.grants.get(grantId)
const grant = await grantex.grants.get('grnt_01J...');grantex.grants.list(params?)
const { grants } = await grantex.grants.list({
agentId: 'ag_01J...', // optional filter
principalId: 'usr_01J...', // optional filter
status: 'active', // 'active' | 'revoked' | 'expired'
page: 1,
pageSize: 20,
});grantex.grants.revoke(grantId)
await grantex.grants.revoke('grnt_01J...');grantex.grants.delegate(params)
Create a delegated sub-agent grant (per SPEC Section 9).
const delegation = await grantex.grants.delegate({
parentGrantToken: 'eyJhbG...',
subAgentId: 'ag_02K...',
scopes: ['files:read'], // must be subset of parent scopes
expiresIn: '1h', // optional, cannot exceed parent
});
// Deliver delegation.grantToken securely to the sub-agent; do not log it.
console.log(delegation.grantId);grantex.grants.verify(token)
Verify a grant token via the API (online verification with real-time revocation check).
const verified = await grantex.grants.verify('eyJhbG...');
console.log(verified.principalId);
console.log(verified.scopes);Throws GrantexTokenError when the token is inactive, revoked, expired, or otherwise unusable.
Tokens
grantex.tokens.exchange(params)
Exchange an authorization code for a grant token. This is the standard way to obtain a grant token after the user approves the consent request.
const token = await grantex.tokens.exchange({
code: 'auth_code_from_redirect', // from your redirect callback
agentId: 'ag_01J...',
});
// Deliver token.grantToken securely to the agent; do not log it.
console.log(token.grantId); // grant record ID
console.log(token.scopes); // granted scopes
console.log(token.expiresAt); // ISO 8601 expiry
// Store token.refreshToken securely for refresh; do not log it.Returns: ExchangeTokenResponse
| Field | Type | Description |
|-------|------|-------------|
| grantToken | string | Signed RS256 JWT — the agent's bearer credential |
| grantId | string | Grant record ID |
| scopes | string[] | Scopes the user approved |
| expiresAt | string | Underlying grant expiry (ISO 8601) |
| refreshToken | string | Refresh token for rotating credentials while the grant remains active |
For a new passkey-approved grant, verifyGrantToken() and grant API responses
expose a compact webauthnEvidence reference. Request
credentialFormat: 'vc-jwt' to receive an opt-in VC with the full assertion in
vc.evidence; grantex.credentials.verify(vcJwt) returns
{ valid, vcId?, payload?, revoked?, expired?, error?, webauthnVerified? }.
Verify the issuer and revocation status, pin the expected RP ID/origin, and
match the evidence digest to the grant reference. The raw credential public
key can correlate VC presentations.
Refresh tokens are single-use and rotate on every accepted refresh. If a response is lost after commit, retry the same previous refresh token and idempotency key. The SDK retains an omitted key for five minutes in the current process; persist an explicit idempotencyKey with the old token when recovery must survive restart or failover. Grantex returns the already-rotated pair without extending expiresAt; after grant expiry, re-authorize.
grantex.tokens.verify(token)
Online token verification with revocation status.
const result = await grantex.tokens.verify('eyJhbG...');
if (result.valid) {
console.log(result.scopes); // ['files:read']
console.log(result.principal); // 'usr_01J...'
console.log(result.agent); // 'ag_01J...'
console.log(result.grantId);
console.log(result.expiresAt);
}grantex.tokens.revoke(tokenId)
Revoke a token by its JTI. Blocklisted in Redis immediately; all sub-delegated tokens are also invalidated.
await grantex.tokens.revoke('tok_01J...');Local Token Verification
verifyGrantToken(token, options)
Verify a grant token locally with RS256 signing keys retrieved from the published JWKS URL. A bounded process-level resolver cache reuses valid keys for each normalized JWKS URI; initial retrieval and key-rotation refreshes may require network access.
import { verifyGrantToken } from '@grantex/sdk';
const grant = await verifyGrantToken('eyJhbG...', {
jwksUri: 'https://api.grantex.dev/.well-known/jwks.json',
issuer: 'https://grantex.dev', // optional when issuer differs from JWKS host
requiredScopes: ['files:read'], // optional — rejects if missing
audience: 'https://myapp.com', // optional — validates aud claim
});If you call a deployment through a raw Cloud Run URL or another internal host, but the service signs tokens for a canonical public domain, pass issuer explicitly. Otherwise issuer validation will reject a valid token because the JWT iss claim will not match the transport host.
Returns: VerifiedGrant
| Field | Type | Description |
|-------|------|-------------|
| tokenId | string | Unique token ID (JWT jti claim) |
| grantId | string | Grant record ID |
| principalId | string | User who authorized the grant (sub claim) |
| agentDid | string | Agent's DID (agt claim) |
| developerId | string | Developer org ID (dev claim) |
| scopes | string[] | Granted scopes (scp claim) |
| issuedAt | number | Issued-at timestamp (seconds since epoch) |
| expiresAt | number | Expiry timestamp (seconds since epoch) |
| parentAgentDid | string? | Parent agent DID (delegation only) |
| parentGrantId | string? | Parent grant ID (delegation only) |
| delegationDepth | number? | Delegation depth (0 = root) |
Audit
grantex.audit.log(params)
Log an auditable action taken by an agent.
const entry = await grantex.audit.log({
agentId: 'ag_01J...',
agentDid: 'did:grantex:ag_01J...',
grantId: 'grnt_01J...',
principalId: 'usr_01J...',
action: 'email:send',
metadata: { to: '[email protected]', subject: 'Hello' },
status: 'success', // 'success' | 'failure' | 'blocked'
});grantex.audit.list(params?)
const { entries } = await grantex.audit.list({
agentId: 'ag_01J...',
action: 'email:send',
since: '2026-01-01T00:00:00Z',
until: '2026-02-28T23:59:59Z',
page: 1,
pageSize: 50,
});grantex.audit.get(entryId)
const entry = await grantex.audit.get('alog_01J...');
console.log(entry.hash); // SHA-256 hash for tamper evidence
console.log(entry.prevHash); // previous entry hash (chain integrity)Webhooks
grantex.webhooks.create(params)
const webhook = await grantex.webhooks.create({
url: 'https://myapp.com/webhooks/grantex',
events: ['grant.created', 'grant.revoked', 'token.issued'],
});
console.log(webhook.secret); // HMAC secret for signature verificationgrantex.webhooks.list()
const { webhooks } = await grantex.webhooks.list();grantex.webhooks.delete(webhookId)
await grantex.webhooks.delete('wh_01J...');Webhook Signature Verification
import { verifyWebhookSignature } from '@grantex/sdk';
// In your webhook handler
verifyWebhookSignature(requestBody, signatureHeader, webhookSecret);Policies
Define fine-grained access control rules for agents.
grantex.policies.create(params)
const policy = await grantex.policies.create({
name: 'Block after hours',
effect: 'deny',
priority: 10,
scopes: ['email:send'],
timeOfDayStart: '18:00',
timeOfDayEnd: '08:00',
});grantex.policies.list()
const { policies, total } = await grantex.policies.list();grantex.policies.get(policyId) / update(policyId, params) / delete(policyId)
const policy = await grantex.policies.get('pol_01J...');
await grantex.policies.update('pol_01J...', { effect: 'allow' });
await grantex.policies.delete('pol_01J...');Compliance
grantex.compliance.getSummary(params?)
const summary = await grantex.compliance.getSummary({
since: '2026-01-01T00:00:00Z',
until: '2026-02-28T23:59:59Z',
});
console.log(summary.agents); // { total, active, suspended, revoked }
console.log(summary.grants); // { total, active, revoked, expired }
console.log(summary.auditEntries); // { total, success, failure, blocked }grantex.compliance.exportGrants(params?)
const { grants, total } = await grantex.compliance.exportGrants({
status: 'active',
});grantex.compliance.exportAudit(params?)
const { entries, total } = await grantex.compliance.exportAudit({
since: '2026-01-01T00:00:00Z',
agentId: 'ag_01J...',
});grantex.compliance.evidencePack(params?)
Generate a full SOC 2 / GDPR evidence pack with audit chain integrity verification.
const pack = await grantex.compliance.evidencePack({
framework: 'soc2', // 'soc2' | 'gdpr' | 'all'
since: '2026-01-01T00:00:00Z',
});
console.log(pack.chainIntegrity.valid); // true
console.log(pack.chainIntegrity.checkedEntries); // 1042
console.log(pack.summary);
console.log(pack.grants);
console.log(pack.auditEntries);
console.log(pack.policies);Anomaly Detection
In version 0.7.0, grantex.anomalies.getResponsePolicy() and
grantex.anomalies.setResponsePolicy('alert_only') control the account-wide
response of the legacy detector when the server operator enables
IRREGULARITY_RESPONSE_POLICY_ENABLED=true. Default behavior revokes active
grants for a high/critical finding's agent. Alert-only retains findings and
events without that automatic revocation. This setting does not disable other
security controls.
Version 0.7.0 also supports
grantex.webauthn.createEnrollmentSession({ principalId, authRequestId? }).
Call it only on your server after authenticating the customer. Deliver the
returned one-use hosted link to that exact customer; never expose the API key
or log the link. Hosted enrollment requires the server's default-off
PASSKEY_ENROLLMENT_ENABLED=true setting. Live consent has no password or
sandbox fallback.
grantex.anomalies.detect()
Run anomaly detection across all agents.
const { anomalies, total } = await grantex.anomalies.detect();
// anomaly types: 'rate_spike' | 'high_failure_rate' | 'new_principal' | 'off_hours_activity'grantex.anomalies.list(params?)
const { anomalies } = await grantex.anomalies.list({
unacknowledged: true, // only open anomalies
});grantex.anomalies.acknowledge(anomalyId)
const anomaly = await grantex.anomalies.acknowledge('anom_01J...');Billing
grantex.billing.getSubscription()
const sub = await grantex.billing.getSubscription();
console.log(sub.plan); // 'free' | 'pro' | 'enterprise'
console.log(sub.status); // 'active' | 'past_due' | 'canceled'
console.log(sub.currentPeriodEnd); // ISO 8601 or nullgrantex.billing.createCheckout(params)
const { checkoutUrl } = await grantex.billing.createCheckout({
plan: 'pro',
successUrl: 'https://myapp.com/billing/success',
cancelUrl: 'https://myapp.com/billing/cancel',
});
// Redirect user to checkoutUrlgrantex.billing.createPortal(params)
const { portalUrl } = await grantex.billing.createPortal({
returnUrl: 'https://myapp.com/settings',
});SCIM 2.0 Provisioning
Sync users from your identity provider.
Token Management
// Create a SCIM bearer token
const { token, id, label } = await grantex.scim.createToken({
label: 'Okta SCIM integration',
});
// token is returned once — store it securely
const { tokens } = await grantex.scim.listTokens();
await grantex.scim.revokeToken('scimtok_01J...');User Operations
// List provisioned users
const { Resources, totalResults } = await grantex.scim.listUsers({
startIndex: 1,
count: 100,
});
// Create a user
const user = await grantex.scim.createUser({
userName: '[email protected]',
displayName: 'Alice',
emails: [{ value: '[email protected]', primary: true }],
});
// Get / Replace / Patch / Delete
const user = await grantex.scim.getUser('scimusr_01J...');
await grantex.scim.replaceUser('scimusr_01J...', { userName: '[email protected]' });
await grantex.scim.updateUser('scimusr_01J...', [
{ op: 'replace', path: 'active', value: false },
]);
await grantex.scim.deleteUser('scimusr_01J...');SSO (OIDC)
grantex.sso.createConfig(params)
const config = await grantex.sso.createConfig({
issuerUrl: 'https://accounts.google.com',
clientId: 'xxx.apps.googleusercontent.com',
clientSecret: 'GOCSPX-...',
redirectUri: 'https://myapp.com/auth/callback',
});grantex.sso.getConfig() / deleteConfig()
const config = await grantex.sso.getConfig();
await grantex.sso.deleteConfig();grantex.sso.getLoginUrl(org)
const { authorizeUrl } = await grantex.sso.getLoginUrl('dev_01J...');
// Redirect user to authorizeUrlgrantex.sso.handleCallback(code, state)
const { email, name, sub, developerId } = await grantex.sso.handleCallback(code, state);Version 0.8.2 types sessionToken as an optional enterprise callback field
when the server enables human SSO sessions. The token is an
opaque bearer credential, not the sessionId audit identifier; keep it out of
logs. Hosted dashboard sign-in currently uses OIDC, and machine API keys
remain valid for automation. See the enterprise SSO guide.
Error Handling
All errors extend GrantexError:
import {
GrantexError, // base class
GrantexApiError, // API returned an error (has statusCode, body, requestId)
GrantexAuthError, // 401/403 — invalid or missing API key
GrantexTokenError, // token verification failed (invalid signature, expired, etc.)
GrantexNetworkError, // network failure (timeout, DNS, connection refused)
} from '@grantex/sdk';
try {
await grantex.agents.get('ag_invalid');
} catch (err) {
if (err instanceof GrantexAuthError) {
console.error('Auth failed:', err.statusCode); // 401 or 403
console.error('Request ID:', err.requestId);
} else if (err instanceof GrantexApiError) {
console.error('API error:', err.statusCode, err.body);
} else if (err instanceof GrantexNetworkError) {
console.error('Network error:', err.message, err.cause);
}
}Requirements
- Node.js 18+
- ESM (
"type": "module"in your package.json, or use dynamicimport())
Links
Scope Enforcement (v0.3.1)
Enforce tool-level permissions on any connector — define your own manifests or use the 53 pre-built ones.
import { Grantex, ToolManifest, Permission } from '@grantex/sdk';
const grantex = new Grantex({ apiKey: 'gx_...' });
// Define a manifest for any connector — no dependency on Grantex to add support
grantex.loadManifest(new ToolManifest({
connector: 'my-crm',
tools: { search: Permission.READ, create_deal: Permission.WRITE, delete_account: Permission.DELETE },
}));
const result = await grantex.enforce({ grantToken: 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, <1mswrapTool()— auto-enforce on LangChain toolsenforceMiddleware()— Express/Fastify HTTP middleware- 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 (
enforceMode: 'permissive')
Grantex Ecosystem
| Package | Description |
|---|---|
| grantex | Python 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 |
License
Ownership
Grantex is owned by Orchestrum Technologies LLP. Inventor and owner: Sanjeev Kumar. Ownership contact: [email protected] or [email protected].
Source Development
Use Node.js 24 LTS and npm ci to build or test this checkout with Vitest 5.
Repository validation steps are in the dependency upgrade guide.
Source-tooling requirements are separate from published package runtime support.
Authority hardening requires TypeScript SDK 0.8.1 (
currentAuthority), 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.
