Skip to content
/ docs Public

Attesso developer documentation. Payment infrastructure for AI agents. Mandate requests, ephemeral card issuance, SDK and API reference.

Notifications You must be signed in to change notification settings

Attesso/docs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 

Repository files navigation

Attesso Documentation

Payment infrastructure for AI agents. Mandate requests + JIT ephemeral card issuance.

Full docs: attesso.com/docs

Quick Start

Go from zero to first mandate in 5 minutes.

Key Concepts

Concept Description
mandate_request Authorization request sent to a user. Contains amount and validity window. Returns approval URL for user consent via passkey.
ephemeral_card Short-lived virtual Visa card issued on demand. Default 5-minute TTL, single-use, auto-destroyed after authorization.
passkey Hardware-backed user authentication. FaceID, TouchID, or Windows Hello. Cryptographically bound to attesso.com.
fee_modes Two modes: markup (default) adds fees on top, inclusive deducts fees from amount.

1. Install

npm install @attesso/sdk
# or
pnpm add @attesso/sdk

2. Initialize

import { AttessoClient } from '@attesso/sdk';

const attesso = new AttessoClient({
  apiKey: process.env.ATTESSO_API_KEY  // rk_test_...
});

3. Create a Mandate Request

Your agent creates a mandate request. The user approves via passkey (FaceID/TouchID). Their payment method is charged immediately.

const request = await attesso.createMandateRequest({
  externalUserId: 'user_123',     // your user identifier
  amount: 50000,                   // $500 in cents
  validityWindow: '24h',           // request expires in 24 hours
});

// Send the approval URL to your user
console.log('Approve at:', request.approvalUrl);
// User authenticates with FaceID/TouchID (WebAuthn passkey)

4. Poll for Approval

const status = await attesso.getMandateRequest(request.id);

if (status.status === 'approved') {
  console.log('Mandate ID:', status.mandate.id);
  console.log('Spending limit:', status.spendingLimit);  // amount minus fees
}

5. Issue a Card

When your agent is ready to make a purchase, issue an ephemeral virtual card.

const card = await attesso.issueCard(status.mandate.id, {
  amount: 34700,       // $347.00
  ttlSeconds: 300,     // 5 min TTL
});

console.log(card.number);    // '4242...'
console.log(card.cvc);       // '123'
console.log(card.expMonth);  // 3
console.log(card.expYear);   // 2026
// Use at merchant checkout. Auto-captured. Card destroyed after use.

1% platform fee + Stripe processing at cost. Optional developer fee (0-5%). Pricing


Vercel AI SDK Integration

The fastest way to add payment capabilities to your AI agent. One import, four tools, instant card issuance.

import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { attesso } from '@attesso/sdk/vercel';

const result = await generateText({
  model: openai('gpt-4o'),
  tools: attesso.tools({ mandateId }),
  prompt: 'Book me a flight to NYC under $500',
});

Installation

npm install @attesso/sdk ai zod

Available Tools

Tool Type Description
attesso_get_mandate read Get mandate details including spendingLimit, remainingLimit, allowed merchants, and status.
attesso_issue_card write Issue an ephemeral virtual card with exact amount and ttlSeconds. Returns card number, cvc, expMonth, expYear.
attesso_get_card read Get card status and details. Check if the card has been used, expired, or cancelled.
attesso_revoke_mandate write Revoke a mandate. No further cards can be issued against it.

Full Example: Travel Agent

import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { attesso } from '@attesso/sdk/vercel';

async function bookFlight(mandateId: string, request: string) {
  const result = await generateText({
    model: openai('gpt-4o'),
    tools: {
      ...attesso.tools({ mandateId }),
      search_flights: {
        description: 'Search for available flights',
        parameters: z.object({
          from: z.string(),
          to: z.string(),
          date: z.string(),
        }),
        execute: async ({ from, to, date }) => {
          return searchFlightsAPI(from, to, date);
        },
      },
    },
    system: `You are a travel booking agent.
1. Search for flights matching the user request
2. Check the mandate with attesso_get_mandate
3. Find the best deal within the spendingLimit
4. Use attesso_issue_card to get a card for the purchase
5. Confirm the booking details`,
    prompt: request,
    maxSteps: 10,
  });

  return result.text;
}

const response = await bookFlight(
  'mandate_abc123',
  'Book me the cheapest flight from SFO to NYC next Friday'
);

Note: The Vercel AI SDK integration requires ai and zod as peer dependencies.


MCP (Model Context Protocol)

Use Attesso payment tools in any MCP-compatible AI assistant: Claude Desktop, Cursor, Windsurf, Continue, Zed, Cline, and more.

Configuration

Add Attesso to your MCP config file:

  • Claude Desktop (macOS): ~/Library/Application Support/Claude/claude_desktop_config.json
  • Claude Desktop (Windows): %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "attesso": {
      "command": "npx",
      "args": ["-y", "@attesso/mcp"],
      "env": {
        "ATTESSO_API_KEY": "rk_test_..."
      }
    }
  }
}

Restart your AI assistant after updating the configuration.

Available Tools

Same 4 tools as the Vercel AI SDK: attesso_get_mandate, attesso_issue_card, attesso_get_card, attesso_revoke_mandate.

Environment Variables

Variable Description
ATTESSO_API_KEY API key (rk_test_* or rk_live_*)
ATTESSO_BASE_URL Custom API URL (default: https://api.attesso.com)
ATTESSO_MANDATE_ID Default mandate ID for all operations
ATTESSO_READ_ONLY Set to "true" to disable write operations

Security Note: Mandate creation requires user passkey authentication (FaceID/TouchID) and cannot be done through MCP. MCP tools only operate within existing, user-authorized mandates.


SDK & API Reference

The @attesso/sdk package provides a TypeScript/JavaScript client for the Attesso API. The SDK uses camelCase; the REST API uses snake_case.

SDK Methods

createMandateRequest(options)

Create a mandate request that the user can approve.

const request = await attesso.createMandateRequest({
  amount: 50000,                    // $500.00 in cents
  externalUserId: 'user_abc',       // your user identifier
  validityWindow: '7d',             // expires in 7 days
  category: 'travel',               // optional category
  metadata: {                       // optional key-value pairs
    agentId: 'travel-bot-001',
    purpose: 'Flight booking',
  },
  callbackUrl: 'https://...',       // optional webhook URL
});

console.log(request.id);              // 'req_abc123'
console.log(request.approvalUrl);     // 'https://attesso.com/approve/req_abc123'
console.log(request.spendingLimit);   // 47000 (amount minus fees)
console.log(request.totalCharged);    // 50000
console.log(request.status);          // 'pending'
console.log(request.callbackSecret);  // only if callbackUrl provided

getMandateRequest(requestId)

const request = await attesso.getMandateRequest('req_abc123');

console.log(request.status);   // 'pending' | 'approved' | 'rejected' | 'expired' | 'cancelled'
console.log(request.mandate);  // { id: '...' } when status is 'approved'

cancelMandateRequest(requestId)

await attesso.cancelMandateRequest('req_abc123');

getMandate(mandateId)

const mandate = await attesso.getMandate('mandate_xyz123');

console.log(mandate.status);          // 'active' | 'exhausted' | 'expired' | 'revoked'
console.log(mandate.spendingLimit);    // 47000 (cents)
console.log(mandate.totalCharged);     // 50000 (what user was charged)
console.log(mandate.expiresAt);        // '2026-03-01T00:00:00Z'

issueCard(mandateId, options)

Issue an ephemeral virtual card against a mandate. Returns full card details (PAN, CVC, expiry).

const card = await attesso.issueCard('mandate_xyz123', {
  amount: 34700,         // $347.00 in cents
  ttlSeconds: 300,       // 5 minute TTL (default)
  allowedMccs: ['3000', '3001', '3299'],  // optional
  // OR blockedMccs: ['7995']              // mutually exclusive
});

console.log(card.cardId);     // 'card_abc456'
console.log(card.number);     // '4242424242424242'
console.log(card.cvc);        // '123'
console.log(card.expMonth);   // 3
console.log(card.expYear);    // 2026
console.log(card.expiresAt);  // '2026-02-07T12:05:00Z'

Important: Card PAN, CVC, and expiry are only returned on issuance. getCard() does not return these fields.

getCard(cardId)

const card = await attesso.getCard('card_abc456');

console.log(card.status);  // 'active' | 'used' | 'expired' | 'cancelled'

getPayment(paymentId)

const payment = await attesso.getPayment('pay_xyz789');

console.log(payment.status);        // 'authorized' | 'captured' | 'reversed' | 'declined'
console.log(payment.amount);        // 34700
console.log(payment.cardId);        // 'card_abc456'
console.log(payment.mandateId);     // 'mandate_xyz123'
console.log(payment.merchantName);  // 'United Airlines'
console.log(payment.merchantMcc);   // '3000'

Error Handling

import { AttessoClient, AttessoError } from '@attesso/sdk';

try {
  const card = await attesso.issueCard('mandate_xyz', {
    amount: 100000,
    ttlSeconds: 300,
  });
} catch (error) {
  if (error instanceof AttessoError) {
    console.log('Status:', error.statusCode);   // 400
    console.log('Code:', error.code);           // 'AMOUNT_EXCEEDS_LIMIT'
    console.log('Message:', error.message);     // 'Amount exceeds mandate spending limit'
  }
}

REST API

Base URL: https://api.attesso.com/v1

The REST API uses snake_case for all field names.

Authentication

curl https://api.attesso.com/v1/mandates/mandate_xyz \
  -H "Authorization: Bearer rk_test_your_restricted_key" \
  -H "Content-Type: application/json"

Endpoints

Method Endpoint Description
POST /v1/mandate-requests Create a new mandate request
GET /v1/mandate-requests/:id Get mandate request status
DELETE /v1/mandate-requests/:id Cancel a pending mandate request
GET /v1/mandates/:id Get mandate details
POST /v1/mandates/:id/cards Issue an ephemeral virtual card
DELETE /v1/mandates/:id Revoke a mandate
GET /v1/cards/:id Get card status (no PAN/CVC)
GET /v1/payments/:id Get payment details
POST /v1/mandate-requests/preview-fees Preview fee calculation

Idempotency

For safe retries, include an Idempotency-Key header with POST requests.

curl -X POST https://api.attesso.com/v1/mandates/mandate_xyz/cards \
  -H "Authorization: Bearer rk_test_..." \
  -H "Idempotency-Key: unique-request-id-12345" \
  -H "Content-Type: application/json" \
  -d '{"amount": 34700, "ttl_seconds": 300}'

Keys expire after 24 hours. Concurrent duplicates return 409 Conflict.

Fees

Two fee modes:

Mode Description Example ($500 amount)
Markup (default) Fees added on top. Input amount = agent spending limit. spendingLimit: $500.00, totalCharged: $519.06
Inclusive Fees deducted from amount. Input amount = what user is charged. totalCharged: $500.00, spendingLimit: $480.94
Component Rate Description
Platform fee 1% Attesso platform fee
Processing at cost Stripe processing (varies by region)
Developer fee 0-5% Optional, routed to your Stripe Connect account

Error Codes

Code Description
MANDATE_NOT_FOUND Mandate ID doesn't exist
MANDATE_REVOKED Mandate has been revoked
MANDATE_EXPIRED Mandate has passed its expiration date
MANDATE_EXHAUSTED Mandate remaining limit is zero
AMOUNT_EXCEEDS_LIMIT Card amount exceeds mandate remaining limit
MERCHANT_NOT_ALLOWED Merchant MCC not in card's allowedMccs or is in blockedMccs
CARD_NOT_FOUND Card ID doesn't exist
CARD_EXPIRED Card TTL has expired
INVALID_AMOUNT Amount must be a positive integer (in cents)
AUTHORIZATION_FAILED Card declined or insufficient funds
WEBAUTHN_VERIFICATION_FAILED Passkey signature verification failed

Error response format:

{
  "error": {
    "code": "AMOUNT_EXCEEDS_LIMIT",
    "message": "Requested amount $600.00 exceeds mandate remaining limit of $500.00",
    "statusCode": 400
  }
}

Webhooks

Webhooks notify your application when events happen. Configure a callbackUrl when creating a mandate request to receive events for that mandate's lifecycle.

Webhook Security: Verify webhook authenticity using the X-Attesso-Signature header with HMAC-SHA256. The callbackSecret is returned once when creating the mandate request. Store it securely.

Event Types

Event Description
mandate_request.approved User approved the mandate request via passkey
mandate_request.rejected User rejected the mandate request
mandate_request.expired Request expired (validity window elapsed)
mandate.exhausted Mandate spending limit fully consumed
mandate.expired Mandate reached its expiration date
payment.captured Payment captured at merchant
payment.reversed Payment was reversed (refund or chargeback)

Webhook Payload

{
  "event": "mandate_request.approved",
  "timestamp": "2026-02-08T12:00:00Z",
  "data": {
    "request_id": "req_abc123",
    "mandate_id": "mandate_xyz",
    "spending_limit": 47000,
    "external_user_id": "user_abc",
    "status": "approved",
    "category": "travel",
    "metadata": { "agent_id": "travel-bot" }
  }
}

Handling Webhooks

import express from 'express';
import crypto from 'crypto';

const app = express();

app.post('/webhooks/attesso', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-attesso-signature'] as string;
  const expectedSignature = crypto
    .createHmac('sha256', process.env.CALLBACK_SECRET!)
    .update(req.body)
    .digest('hex');

  if (signature !== expectedSignature) {
    return res.status(400).send('Invalid signature');
  }

  const event = JSON.parse(req.body.toString());

  switch (event.event) {
    case 'mandate_request.approved':
      console.log('Mandate approved:', event.data.mandate_id);
      break;
    case 'payment.captured':
      console.log('Payment captured:', event.data.payment_id);
      break;
    case 'mandate.exhausted':
      console.log('Mandate exhausted:', event.data.mandate_id);
      break;
  }

  res.json({ received: true });
});

Testing & Go Live

Test API Keys

Test keys start with rk_test_. Cards issued with test keys are simulated and don't interact with the Visa network.

Test Mode Behavior

  • WebAuthn verification is bypassed in test mode
  • Cards are simulated and don't hit the Visa network
  • Webhooks fire normally (configure a callbackUrl)
  • Fee calculations work identically to production
  • Rate limits are relaxed for easier development

Go Live Checklist

  1. Complete your profile:Fill in business details in the dashboard settings
  2. Connect your bank account:Click "Go Live" in dashboard, follow Stripe Connect onboarding
  3. Switch to live keys:Replace rk_test_... with rk_live_... in production
  4. Start issuing:Mandates authorize real cards, funds settle to your bank via Stripe

Payouts are sent to your bank account on a 2-day rolling basis.


Security

Attesso uses WebAuthn passkeys to ensure every mandate is authorized by a real user on a real device. Hardware-backed keys prevent bot farms, emulators, and phishing at the hardware level.

No app required. Users authorize via WebAuthn passkeys directly in the browser.

Hardware Security by Platform

Device Hardware Biometric Key Storage
iPhone Secure Enclave FaceID / TouchID SE P256
Mac (TouchID) Secure Enclave (T2/M-series) TouchID SE P256
Windows TPM 2.0 Windows Hello TPM RSA/ECC
Android StrongBox / TEE Fingerprint / Face TEE P256

Security Properties

  • Passkey attestation: Signature from TPM/Secure Enclave chip, not browser. Cannot be faked with JavaScript.
  • Phishing resistant: Passkey is cryptographically bound to attesso.com. Fake domains don't trigger the prompt.
  • Key isolation: Private key never leaves device hardware. Even if phone is cloned, key cannot be extracted.

Rate Limiting

Endpoint Limit
Auth 5/min
Card issuance 30/min
General 100/min

Sliding window. Check X-RateLimit-* response headers.

Ephemeral Card Security

  • Cards have a short TTL (30s-3600s, default 300s)
  • Single-use: destroyed after first merchant authorization
  • Amount-limited: card cannot be charged more than issued amount
  • MCC-restricted: allowedMccs or blockedMccs per card
  • Auto-destroyed on expiry even if unused
  • PAN/CVC only returned on issuance, not on subsequent GET

Links


Built for the agent economy. Hardware-secured. No app required.

About

Attesso developer documentation. Payment infrastructure for AI agents. Mandate requests, ephemeral card issuance, SDK and API reference.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published