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".
Code
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
Code
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).
Code
pip install "httpx==0.28.*"
Save this as tomba_mpp.py:
Code
"""Call agents.tomba.io and pay for each request through InFlow."""import base64import jsonimport osimport reimport timeimport httpxAGENTS_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 challengesdef _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()
from langchain.agents import create_agentfrom langchain.tools import toolfrom tomba_mpp import paid_get@tooldef 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})@tooldef 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
Code
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, Taskfrom crewai.tools import toolfrom 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:
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.