TombaTomba
Start free
  • Documentation
  • API reference
  • SDKs & CLI
  • AI & MCP
  • Integrations
  • Enterprise
  • Migration guides

Find, verify and enrich B2B contacts from public sources, in the app, your spreadsheet or through the API.

GDPR & CCPA

// docs

  • API changelog
  • Support options
  • Security

// platform

  • Dashboard
  • Pricing
  • API status

// resources

  • Help center
  • Product updates
  • TombaBot crawler

// company

  • Website
  • Contact us
  • Edit or remove your data

© 2021–2026 Tomba.io

  • Terms of service
  • Privacy policy
  • Cookie policy
  • Security policy
  • GDPR
  • CCPA
Remote MCP server
Local MCP serverDocs for AI assistants
Agent payments (MPP)
    OverviewQuickstartUse with AI agents
Agent payments (MPP)

Use with AI agents

The examples on this page give an agent Tomba tools that call agents.tomba.io and pay for each request from an InFlow balance. No Tomba account or API key is involved. Each example has two parts: a helper that makes a paid request, and tools for your agent framework that call the helper.

Before you begin

  • An InFlow account in the production environment, with a USDC or USDT balance and an API key. Put the key in the INFLOW_API_KEY environment variable.
  • An API key for your model provider: ANTHROPIC_API_KEY for the Vercel AI SDK and LangChain examples, and OPENAI_API_KEY for CrewAI's default model.

The examples were checked against @inflowpayai/mpp-buyer 0.7.3, mppx 0.8.19, ai 7.0.122, @ai-sdk/anthropic 4.0.68, httpx 0.28.1, langchain 1.4.3, langchain-anthropic 1.7.5, and crewai 1.15.23.

JavaScript

The JavaScript helper uses InFlow's buyer SDK, which needs Node.js 22 or later. Node.js 22.18 and later run the TypeScript files below directly; put them in a project whose package.json sets "type": "module".

TerminalCode
npm install @inflowpayai/mpp-buyer@0.7 mppx@0.8

Payment-aware fetch

mppx.fetch sends the request, pays the 402 challenge through InFlow, and sends the request again with the credential. Save this as tomba.ts:

Code
import { Mppx, inflow } from "@inflowpayai/mpp-buyer"; const apiKey = process.env.INFLOW_API_KEY; if (!apiKey) throw new Error("Set INFLOW_API_KEY"); const mppx = Mppx.create({ // Pay only through mppx.fetch; leave globalThis.fetch unchanged. polyfill: false, methods: [inflow({ apiKey, environment: "production" })], }); // Calls an agents.tomba.io endpoint and pays for it when it answers 402. export async function tomba( path: string, params: Record<string, string>, ): Promise<unknown> { const url = new URL(path, "https://agents.tomba.io"); url.search = new URLSearchParams(params).toString(); const response = await mppx.fetch(url); const body: unknown = await response.json(); if (!response.ok) { const charged = response.headers.get("X-Payment-Charged"); throw new Error( `Tomba returned ${response.status} (charged: ${charged}): ${JSON.stringify(body)}`, ); } return body; }

A payment that InFlow rejects makes mppx.fetch throw. To record what you paid, read the receipt of a successful response with Receipt.fromResponse(response), which @inflowpayai/mpp-buyer exports.

Vercel AI SDK

TerminalCode
npm install ai@7 @ai-sdk/anthropic@4 zod@4

Save this as agent.ts next to tomba.ts, and run it with node agent.ts:

Code
import { anthropic } from "@ai-sdk/anthropic"; import { generateText, stepCountIs, tool } from "ai"; import { z } from "zod"; import { tomba } from "./tomba.ts"; const result = await generateText({ model: anthropic("claude-opus-5-5"), tools: { domain_search: tool({ description: "Find email addresses at a company from its domain.", inputSchema: z.object({ domain: z .string() .describe("Company domain, for example stripe.com"), }), execute: ({ domain }) => tomba("/domain-search", { domain }), }), email_verifier: tool({ description: "Check whether an email address is deliverable.", inputSchema: z.object({ email: z.string().email() }), execute: ({ email }) => tomba("/email-verifier", { email }), }), }, stopWhen: stepCountIs(5), prompt: "Find an engineering contact at stripe.com and verify the address.", }); console.log(result.text);

Python

The Python examples need Python 3.10 or later.

Python helper

The helper makes the calls described in Pay without the CLI. It pays in the currency set in TOMBA_MPP_CURRENCY (USDC if unset).

TerminalCode
pip install "httpx==0.28.*"

Save this as tomba_mpp.py:

Code
"""Call agents.tomba.io and pay for each request through InFlow.""" import base64 import json import os import re import time import httpx AGENTS_URL = "https://agents.tomba.io" INFLOW_URL = "https://api.inflowpay.ai" INFLOW_API_KEY = os.environ["INFLOW_API_KEY"] CURRENCY = os.environ.get("TOMBA_MPP_CURRENCY", "USDC") def _payment_challenges(header): """Parse the Payment challenges in a WWW-Authenticate value.""" challenges = [] for part in re.split(r",\s*(?=Payment\s)", header): scheme, _, params = part.strip().partition(" ") if scheme == "Payment": pairs = re.findall(r'(\w+)="((?:[^"\\]|\\.)*)"', params) challenges.append({k: re.sub(r"\\(.)", r"\1", v) for k, v in pairs}) return challenges def _currency(challenge): encoded = challenge["request"] padded = encoded + "=" * (-len(encoded) % 4) return json.loads(base64.urlsafe_b64decode(padded))["currency"] def _pay(challenge): """Fulfil a challenge with InFlow and return the payment credential.""" headers = {"X-API-KEY": INFLOW_API_KEY} response = httpx.post( f"{INFLOW_URL}/v1/transactions/mpp", headers=headers, json={"challenge": challenge, "options": {}}, timeout=30, ) response.raise_for_status() transaction = response.json() while transaction["state"] == "pending": time.sleep(transaction.get("retryAfterSeconds", 5)) response = httpx.get( f"{INFLOW_URL}/v1/transactions/{transaction['transactionId']}/mpp", headers=headers, timeout=30, ) response.raise_for_status() transaction = response.json() if transaction["state"] != "ready": raise RuntimeError(f"InFlow payment {transaction['state']}") return transaction["credential"] def paid_get(path, params): """GET an agents.tomba.io endpoint, paying when it answers 402.""" url = f"{AGENTS_URL}{path}" response = httpx.get(url, params=params, timeout=60) if response.status_code == 402: offers = _payment_challenges(response.headers["WWW-Authenticate"]) challenge = next(c for c in offers if _currency(c) == CURRENCY) credential = _pay(challenge) response = httpx.get( url, params=params, headers={"Authorization": f"Payment {credential}"}, timeout=60, ) if response.is_error: charged = response.headers.get("X-Payment-Charged") raise RuntimeError( f"Tomba returned {response.status_code} (charged: {charged}): {response.text}" ) return response.json()

LangChain

TerminalCode
pip install "langchain==1.4.*" "langchain-anthropic==1.7.*"
Code
from langchain.agents import create_agent from langchain.tools import tool from tomba_mpp import paid_get @tool def domain_search(domain: str) -> dict: """Find email addresses at a company from its domain, for example stripe.com.""" return paid_get("/domain-search", {"domain": domain}) @tool def email_verifier(email: str) -> dict: """Check whether an email address is deliverable.""" return paid_get("/email-verifier", {"email": email}) agent = create_agent("anthropic:claude-opus-5-5", tools=[domain_search, email_verifier]) if __name__ == "__main__": result = agent.invoke( {"messages": [{"role": "user", "content": "Find an engineering contact at stripe.com and verify the address."}]} ) print(result["messages"][-1].text)

CrewAI

TerminalCode
pip install "crewai==1.15.*"

The agent uses CrewAI's default model. To use another one, pass llm to Agent.

Code
from crewai import Agent, Crew, Task from crewai.tools import tool from tomba_mpp import paid_get @tool("Domain search") def domain_search(domain: str) -> dict: """Find email addresses at a company from its domain, for example stripe.com.""" return paid_get("/domain-search", {"domain": domain}) @tool("Email verifier") def email_verifier(email: str) -> dict: """Check whether an email address is deliverable.""" return paid_get("/email-verifier", {"email": email}) researcher = Agent( role="Lead researcher", goal="Find and verify contacts at target companies", backstory="You research B2B contacts with Tomba.", tools=[domain_search, email_verifier], ) task = Task( description="Find an engineering contact at stripe.com and verify the address.", expected_output="The contact's name, email address, and verification status.", agent=researcher, ) if __name__ == "__main__": print(Crew(agents=[researcher], tasks=[task]).kickoff())

Choose a currency

Each 402 offers one challenge in USDC and one in USDT, and your InFlow account must hold the currency of the challenge you pay.

  • The Python helper pays in TOMBA_MPP_CURRENCY. It raises StopIteration when no challenge offers that currency.
  • mppx.fetch pays the first challenge its methods can handle. To prefer a currency, pass orderChallenges to Mppx.create; it receives the candidate challenges and returns them in the order to try:
Code
import { Mppx, inflow } from "@inflowpayai/mpp-buyer"; const apiKey = process.env.INFLOW_API_KEY; if (!apiKey) throw new Error("Set INFLOW_API_KEY"); const mppx = Mppx.create({ polyfill: false, methods: [inflow({ apiKey, environment: "production" })], // Try the USDT challenge first. orderChallenges: (candidates) => [...candidates].sort( (a, b) => Number(b.challenge.request.currency === "USDT") - Number(a.challenge.request.currency === "USDT"), ), });

Errors and billing

Both helpers raise an error when the final response has an error status. The message includes the status, the value of X-Payment-Charged, and Tomba's response body. A response with X-Payment-Charged: false cost nothing. Billing and Errors list every outcome.

Each paid request is charged, including repeats, so store results your agent may need again instead of requesting them twice.

Last modified on September 30, 2026
Quickstart
On this page
  • Before you begin
  • JavaScript
    • Payment-aware fetch
    • Vercel AI SDK
  • Python
    • Python helper
    • LangChain
    • CrewAI
  • Choose a currency
  • Errors and billing
TypeScript
TypeScript
TypeScript