Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.grantex.dev/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This guide walks through integrating Grantex agent passports with the Machine Payments Protocol (MPP). You’ll learn how to:
  1. Agent side — Issue passports and attach them to outgoing MPP requests
  2. Merchant side — Verify passports on incoming requests with one line of code
  3. Trust registry — Look up organization trust levels before fulfilling requests

Prerequisites

  • A Grantex account with an API key (sign up free)
  • A registered agent with an active grant that includes payments:mpp:* scopes
  • Node.js 18+ for the TypeScript SDK

Installation

npm install @grantex/sdk @grantex/mpp

Agent Side: Issue and Attach Passports

Step 1: Issue a Passport

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

const grantex = new Grantex({ apiKey: process.env.GRANTEX_API_KEY });

// Issue a passport for your agent
const passport = await grantex.passports.issue({
  agentId: 'ag_01HXYZ...',
  grantId: 'grnt_01HXYZ...',
  allowedMPPCategories: ['inference', 'compute'],
  maxTransactionAmount: { amount: 50, currency: 'USDC' },
  paymentRails: ['tempo'],
  expiresIn: '24h',  // max: 720h (30 days)
});

console.log(passport.passportId);
// "urn:grantex:passport:01HXYZ..."
The grant must include the corresponding payments:mpp:* scopes for each category. For example, requesting ['inference', 'compute'] requires the grant to have payments:mpp:inference and payments:mpp:compute scopes.

Step 2: Attach to MPP Requests

Use the middleware to automatically inject the X-Grantex-Passport header:
import { createMppPassportMiddleware } from '@grantex/mpp';

const middleware = createMppPassportMiddleware({
  passport,
  autoRefreshThreshold: 300, // warn 5 min before expiry
});

// Wrap every outgoing fetch
const request = new Request('https://merchant.example.com/api/inference', {
  method: 'GET',
  headers: { 'Authorization': 'Payment <mpp-token>' },
});

const enrichedRequest = await middleware(request);
const response = await fetch(enrichedRequest);
// enrichedRequest has X-Grantex-Passport header attached

Merchant Side: Verify Passports

One line to protect any route:
import express from 'express';
import { requireAgentPassport } from '@grantex/mpp';

const app = express();

// Protect inference endpoints — require inference category
app.use('/api/inference', requireAgentPassport({
  requiredCategories: ['inference'],
  maxAmount: 10,
}));

app.get('/api/inference', (req, res) => {
  // req.agentPassport is populated
  console.log(req.agentPassport.humanDID);        // "did:grantex:user_alice"
  console.log(req.agentPassport.organizationDID);  // "did:web:acme.com"
  console.log(req.agentPassport.allowedCategories); // ["inference", "compute"]

  res.json({ result: 'inference complete' });
});
Invalid passports return 403 with a typed error code:
{
  "error": "CATEGORY_MISMATCH",
  "message": "Passport does not cover required categories: storage"
}

Option B: Standalone Verification

import { verifyPassport } from '@grantex/mpp';

const encodedPassport = req.headers['x-grantex-passport'];

try {
  const verified = await verifyPassport(encodedPassport, {
    trustedIssuers: ['did:web:grantex.dev'],
    requiredCategories: ['inference'],
    maxAmount: 10,
    checkRevocation: false,  // true for online revocation check
  });

  console.log(verified.valid);          // true
  console.log(verified.humanDID);       // "did:grantex:user_alice"
  console.log(verified.agentDID);       // "did:grantex:ag_01HXYZ..."
  console.log(verified.grantId);        // "grnt_01HXYZ..."
  console.log(verified.delegationDepth); // 0
} catch (err) {
  // err.code is a typed PassportErrorCode
  console.error(err.code, err.message);
}

Trust Registry Lookup

Before fulfilling a high-value request, check the organization’s trust level:
import { lookupOrgTrust } from '@grantex/mpp';

const record = await lookupOrgTrust(verified.organizationDID);

if (!record || record.trustLevel === 'basic') {
  // Require additional verification for unverified orgs
  return res.status(403).json({ error: 'ORG_NOT_VERIFIED' });
}

// record.trustLevel: "basic" | "verified" | "soc2"
// record.verificationMethod: "dns-txt" | "manual" | "soc2"
Results are cached in-memory for 1 hour by default.

Revoking Passports

Instantly revoke a passport — flips the StatusList2021 bit:
const result = await grantex.passports.revoke('urn:grantex:passport:01HXYZ...');
// { revoked: true, revokedAt: "2026-03-20T..." }
After revocation, any verifyPassport() call with checkRevocation: true will throw PASSPORT_REVOKED.

Verification Options Reference

OptionTypeDefaultDescription
jwksUristringhttps://api.grantex.dev/.well-known/jwks.jsonJWKS endpoint for signature verification
trustedIssuersstring[]['did:web:grantex.dev']Allowed issuer DIDs
requiredCategoriesMPPCategory[]undefinedRequired MPP categories
maxAmountnumberundefinedMinimum passport amount ceiling
checkRevocationbooleanfalseOnline revocation check via StatusList2021
revocationEndpointstringRequired when checkRevocation: true

Next Steps