JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 1155
  • Score
    100M100P100Q87180F
  • License Apache-2.0

TypeScript SDK for the Grantex delegated authorization protocol

Package Exports

  • @grantex/sdk

Readme

@grantex/sdk

TypeScript SDK for the Grantex delegated authorization protocol — OAuth 2.0 for AI agents.

npm version License

Homepage | Docs | API Reference | Sign Up Free | GitHub

Installation

npm install @grantex/sdk

Quick Start

import { Grantex, verifyGrantToken } from '@grantex/sdk';

const grantex = new Grantex({ apiKey: 'YOUR_API_KEY' });

// 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.id,
  userId: 'usr_01J...',
  scopes: ['email:read', 'email:send'],
});
// 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.id });
console.log(token.grantToken);  // RS256-signed JWT
console.log(token.scopes);     // ['email:read', 'email:send']
console.log(token.grantId);    // 'grnt_01J...'

// 4. Verify the grant token offline (no network call)
const grant = await verifyGrantToken(token.grantToken, {
  jwksUri: 'https://api.grantex.dev/.well-known/jwks.json',
});
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
  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
timeout number 30000 Request timeout in milliseconds

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'],
  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 timestamp

Returns: 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, total } = 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, total } = 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
});

console.log(delegation.grantToken); // new JWT for the sub-agent
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);

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...',
});

console.log(token.grantToken);   // RS256-signed JWT — pass this to your agent
console.log(token.grantId);      // grant record ID
console.log(token.scopes);       // granted scopes
console.log(token.expiresAt);    // ISO 8601 expiry
console.log(token.refreshToken); // for token refresh

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 Token expiry (ISO 8601)
refreshToken string Refresh token for obtaining new grant tokens

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...');

Offline Token Verification

verifyGrantToken(token, options)

Verify a grant token offline using the published JWKS. No API call needed — signature is verified locally using RS256.

import { verifyGrantToken } from '@grantex/sdk';

const grant = await verifyGrantToken('eyJhbG...', {
  jwksUri: 'https://api.grantex.dev/.well-known/jwks.json',
  requiredScopes: ['files:read'],   // optional — rejects if missing
  audience: 'https://myapp.com',    // optional — validates aud claim
});

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...',
  grantId: 'grnt_01J...',
  action: 'email:send',
  metadata: { to: 'user@example.com', subject: 'Hello' },
  status: 'success',   // 'success' | 'failure' | 'blocked'
});

grantex.audit.list(params?)

const { entries, total } = 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('aud_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 verification

grantex.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

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 null

grantex.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 checkoutUrl

grantex.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: 'alice@example.com',
  displayName: 'Alice',
  emails: [{ value: 'alice@example.com', primary: true }],
});

// Get / Replace / Patch / Delete
const user = await grantex.scim.getUser('scimusr_01J...');

await grantex.scim.replaceUser('scimusr_01J...', { userName: 'alice@new.com' });

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 authorizeUrl

grantex.sso.handleCallback(code, state)

const { email, name, sub, developerId } = await grantex.sso.handleCallback(code, state);

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 dynamic import())

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

Apache 2.0