Skip to main content
Enhanced-tier requests include the original signature alongside the JWT. This gives you independent cryptographic proof of the user’s authorisation — you can verify the signed intent directly without relying solely on the SIS attestation.
You only need this for Enhanced tier (requests with sr:us:pint:spend:execute scope). If the JWT’s verification_tier is "standard", skip this page — standard JWT verification is sufficient.

What You Receive

Enhanced-tier requests include three identity-related headers:
x-sumvin-pint-token: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...
X-Pint-Signature: 0xabc123...
X-Pint-Payload: eyJ3YWxsZXQiOiIweEUyM2M5QTcwQkM3NDlFQmRkZDhj...
See the Header Reference for full format details.

Verification Steps

After completing all standard JWT verification steps, perform these additional checks:

1. Decode the PINT Payload

The X-Pint-Payload header contains the base64-encoded PINT data:
const pintPayloadB64 = request.headers.get("x-pint-payload");
if (!pintPayloadB64) {
  throw new Error("Missing X-Pint-Payload header for enhanced-tier request");
}

const pintPayload = JSON.parse(
  Buffer.from(pintPayloadB64, "base64").toString("utf-8")
);
import base64
import json

pint_payload_b64 = request.headers.get("X-Pint-Payload")
if not pint_payload_b64:
    raise ValueError("Missing X-Pint-Payload header for enhanced-tier request")

pint_payload = json.loads(base64.b64decode(pint_payload_b64).decode("utf-8"))

2. Reconstruct the EIP-712 Hash

Rebuild the EIP-712 typed data hash from the decoded PINT payload:
import { hashTypedData } from "viem";

const domain = {
  name: "Sumvin Purchase Intent",
  version: "1",
  chainId: BigInt(pintPayload.chainId || 1329),
  verifyingContract: pintPayload.wallet as `0x${string}`,
} as const;

const types = {
  PurchaseIntent: [
    { name: "wallet", type: "address" },
    { name: "nonce", type: "uint256" },
    { name: "statement", type: "string" },
    { name: "scopes", type: "string[]" },
    { name: "resources", type: "string[]" },
    { name: "maxAmount", type: "uint256" },
    { name: "maxAmountToken", type: "address" },
    { name: "expiresAt", type: "uint256" },
  ],
} as const;

const message = {
  wallet: pintPayload.wallet as `0x${string}`,
  nonce: BigInt(pintPayload.nonce),
  statement: pintPayload.statement,
  scopes: pintPayload.scopes,
  resources: pintPayload.resources,
  maxAmount: BigInt(pintPayload.maxAmount),
  maxAmountToken: pintPayload.maxAmountToken as `0x${string}`,
  expiresAt: BigInt(pintPayload.expiresAt),
};

const hash = hashTypedData({ domain, types, primaryType: "PurchaseIntent", message });

3. Verify the Signature

Recover the signer address from the X-Pint-Signature header and confirm it matches the wallet in the PINT:
import { recoverTypedDataAddress } from "viem";

const pintSignature = request.headers.get("x-pint-signature") as `0x${string}`;

const recoveredAddress = await recoverTypedDataAddress({
  domain,
  types,
  primaryType: "PurchaseIntent",
  message,
  signature: pintSignature,
});

// For user-signed PINTs (secp256k1), the recovered address should match
// the user's EOA or be a valid signer on the Safe
if (recoveredAddress.toLowerCase() !== expectedSignerAddress.toLowerCase()) {
  throw new Error("PINT signature verification failed");
}
from eth_account.messages import encode_typed_data
from eth_account import Account

# Reconstruct and encode the typed data
signable = encode_typed_data(domain_data, message_types, message_data, "PurchaseIntent")

# Recover the signer
pint_signature = request.headers.get("X-Pint-Signature")
recovered = Account.recover_message(signable, signature=bytes.fromhex(pint_signature[2:]))

if recovered.lower() != expected_signer.lower():
    raise ValueError("PINT signature verification failed")

4. Cross-Check Against the JWT

Confirm that the pint_signature claim in the JWT matches the X-Pint-Signature header. This ensures the JWT and the PINT signature are referring to the same authorisation:
if (jwtPayload.pint_signature !== pintSignature) {
  throw new Error("JWT pint_signature claim does not match X-Pint-Signature header");
}

Complete Enhanced Verification

function verifyPintSignature(
  request: Request,
  jwtPayload: jose.JWTPayload
): void {
  // 1. Get headers
  const pintSignature = request.headers.get("x-pint-signature");
  const pintPayloadB64 = request.headers.get("x-pint-payload");

  if (!pintSignature || !pintPayloadB64) {
    throw new Error("Enhanced tier requires X-Pint-Signature and X-Pint-Payload headers");
  }

  // 2. Decode payload
  const pintPayload = JSON.parse(
    Buffer.from(pintPayloadB64, "base64").toString("utf-8")
  );

  // 3. Reconstruct EIP-712 hash and verify signature
  const recoveredAddress = recoverTypedDataAddress({
    domain: {
      name: "Sumvin Purchase Intent",
      version: "1",
      chainId: BigInt(1329),
      verifyingContract: pintPayload.wallet,
    },
    types: { PurchaseIntent: [/* ... full type array ... */] },
    primaryType: "PurchaseIntent",
    message: {
      wallet: pintPayload.wallet,
      nonce: BigInt(pintPayload.nonce),
      statement: pintPayload.statement,
      scopes: pintPayload.scopes,
      resources: pintPayload.resources,
      maxAmount: BigInt(pintPayload.maxAmount),
      maxAmountToken: pintPayload.maxAmountToken,
      expiresAt: BigInt(pintPayload.expiresAt),
    },
    signature: pintSignature,
  });

  // 4. Verify signer matches the wallet in the JWT
  if (recoveredAddress.toLowerCase() !== (jwtPayload.wallet as string).toLowerCase()) {
    throw new Error("PINT signature does not match JWT wallet");
  }

  // 5. Cross-check JWT claim
  if (jwtPayload.pint_signature !== pintSignature) {
    throw new Error("JWT pint_signature does not match header");
  }
}
For agent-signed PINTs (signer_type: "agent"), standard ECDSA recovery won’t work. Agent keys use P-256, and verification happens via EIP-1271 isValidSignature on the Safe contract. If you need to verify agent-signed PINTs, you’ll need to make an on-chain call to the Safe contract.