Guides
Using MPP with AI Agents
Overview
MPP enables AI agents to access Tomba's contact intelligence tools without API keys. The agent pays per request using the MPP protocol. This is ideal for autonomous agents that need to discover emails, verify contacts, or enrich company data as part of their workflow.
OpenAI Function Calling
Use Tomba MPP endpoints as tools in an OpenAI agent. The agent calls the tool, the tool handles the MPP payment flow, and returns the data.
Codeimport OpenAI from "openai"; import { Mppx, inflow } from "@inflowpayai/mpp-buyer"; const openai = new OpenAI(); // Initialize MPP buyer const mppx = Mppx.create({ methods: [ inflow({ apiKey: process.env.INFLOW_API_KEY, environment: "sandbox", }), ], }); // Define Tomba tools for the agent const tools = [ { type: "function", function: { name: "domain_search", description: "Find all email addresses associated with a company domain", parameters: { type: "object", properties: { domain: { type: "string", description: "The company domain (e.g., tomba.io)", }, }, required: ["domain"], }, }, }, { type: "function", function: { name: "email_finder", description: "Find a specific person's email address from their name and company domain", parameters: { type: "object", properties: { domain: { type: "string", description: "The company domain", }, first_name: { type: "string" }, last_name: { type: "string" }, }, required: ["domain", "first_name", "last_name"], }, }, }, { type: "function", function: { name: "email_verifier", description: "Verify if an email address is valid and deliverable", parameters: { type: "object", properties: { email: { type: "string", description: "The email address to verify", }, }, required: ["email"], }, }, }, { type: "function", function: { name: "company_enrich", description: "Get detailed company information from a domain name", parameters: { type: "object", properties: { domain: { type: "string", description: "The company domain", }, }, required: ["domain"], }, }, }, ]; // Tool executor — handles MPP payment automatically async function executeTool(name, args) { const endpoints = { domain_search: `domain-search?domain=${args.domain}`, email_finder: `email-finder?domain=${args.domain}&first_name=${args.first_name}&last_name=${args.last_name}`, email_verifier: `email-verifier/${args.email}`, company_enrich: `companies/find?domain=${args.domain}`, }; const url = `https://agents.tomba.io/${endpoints[name]}`; const response = await mppx.fetch(url); return await response.json(); } // Run the agent async function runAgent(userMessage) { const messages = [ { role: "system", content: "You are a sales research assistant. Use the available tools to find and verify contact information.", }, { role: "user", content: userMessage }, ]; let response = await openai.chat.completions.create({ model: "gpt-4o", messages, tools, }); // Process tool calls while (response.choices[0].message.tool_calls) { const toolCalls = response.choices[0].message.tool_calls; messages.push(response.choices[0].message); for (const toolCall of toolCalls) { const args = JSON.parse(toolCall.function.arguments); const result = await executeTool(toolCall.function.name, args); messages.push({ role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(result), }); } response = await openai.chat.completions.create({ model: "gpt-4o", messages, tools, }); } return response.choices[0].message.content; } // Example usage const result = await runAgent( "Find the email addresses for people at tomba.io and verify the first one", ); console.log(result);
Anthropic Claude Tool Use
Codeimport Anthropic from "@anthropic-ai/sdk"; import { Mppx, inflow } from "@inflowpayai/mpp-buyer"; const anthropic = new Anthropic(); const mppx = Mppx.create({ methods: [ inflow({ apiKey: process.env.INFLOW_API_KEY, environment: "sandbox", }), ], }); const tools = [ { name: "domain_search", description: "Find all email addresses associated with a company domain", input_schema: { type: "object", properties: { domain: { type: "string", description: "The company domain (e.g., tomba.io)", }, }, required: ["domain"], }, }, { name: "email_verifier", description: "Verify if an email address is valid and deliverable", input_schema: { type: "object", properties: { email: { type: "string" }, }, required: ["email"], }, }, ]; async function executeTool(name, input) { const endpoints = { domain_search: `domain-search?domain=${input.domain}`, email_verifier: `email-verifier/${input.email}`, }; const url = `https://agents.tomba.io/${endpoints[name]}`; const response = await mppx.fetch(url); return await response.json(); } async function runAgent(userMessage) { let messages = [{ role: "user", content: userMessage }]; let response = await anthropic.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 4096, system: "You are a sales research assistant. Use tools to find and verify contacts.", messages, tools, }); while (response.stop_reason === "tool_use") { const toolUseBlocks = response.content.filter( (b) => b.type === "tool_use", ); messages.push({ role: "assistant", content: response.content }); const toolResults = []; for (const block of toolUseBlocks) { const result = await executeTool(block.name, block.input); toolResults.push({ type: "tool_result", tool_use_id: block.id, content: JSON.stringify(result), }); } messages.push({ role: "user", content: toolResults }); response = await anthropic.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 4096, system: "You are a sales research assistant. Use tools to find and verify contacts.", messages, tools, }); } return response.content[0].text; } const result = await runAgent("Find emails at stripe.com"); console.log(result);
LangChain Agent
Codefrom langchain_openai import ChatOpenAI from langchain.agents import AgentExecutor, create_tool_calling_agent from langchain_core.tools import tool from langchain_core.prompts import ChatPromptTemplate import httpx import time TOMBA_MPP_BASE = "https://agents.tomba.io" INFLOW_BASE = "https://sandbox.inflowpay.ai" INFLOW_API_KEY = "your_inflow_api_key" def mpp_fetch(endpoint: str) -> dict: """Handle MPP payment flow and return API response.""" url = f"{TOMBA_MPP_BASE}/{endpoint}" # Step 1: Get 402 challenge resp = httpx.get(url) if resp.status_code != 402: return resp.json() challenge = resp.json()["challenge"] # Step 2: Create payment tx = httpx.post( f"{INFLOW_BASE}/v1/transactions/mpp", headers={"X-API-KEY": INFLOW_API_KEY}, json={"challenge": challenge}, ).json() # Step 3: Poll until ready while True: status = httpx.get( f"{INFLOW_BASE}/v1/transactions/{tx['transactionId']}/mpp", headers={"X-API-KEY": INFLOW_API_KEY}, ).json() if status["state"] == "ready": break if status["state"] in ("failed", "expired"): return {"error": f"Payment {status['state']}"} time.sleep(2) # Step 4: Retry with credential return httpx.get( url, headers={"Authorization": f"Payment {status['credential']}"} ).json() @tool def domain_search(domain: str) -> dict: """Find all email addresses associated with a company domain.""" return mpp_fetch(f"domain-search?domain={domain}") @tool def email_finder(domain: str, first_name: str, last_name: str) -> dict: """Find a person's email address from their name and company domain.""" return mpp_fetch( f"email-finder?domain={domain}&first_name={first_name}&last_name={last_name}" ) @tool def email_verifier(email: str) -> dict: """Verify if an email address is valid and deliverable.""" return mpp_fetch(f"email-verifier/{email}") @tool def company_enrich(domain: str) -> dict: """Get detailed company information from a domain name.""" return mpp_fetch(f"companies/find?domain={domain}") # Create agent llm = ChatOpenAI(model="gpt-4o") prompt = ChatPromptTemplate.from_messages( [ ( "system", "You are a sales research assistant. Use tools to find and verify contacts.", ), ("human", "{input}"), ("placeholder", "{agent_scratchpad}"), ] ) tools = [domain_search, email_finder, email_verifier, company_enrich] agent = create_tool_calling_agent(llm, tools, prompt) executor = AgentExecutor(agent=agent, tools=tools, verbose=True) # Run result = executor.invoke( {"input": "Find emails at tomba.io and verify the first result"} ) print(result["output"])
CrewAI
Codefrom crewai import Agent, Task, Crew from crewai.tools import tool import httpx import time TOMBA_MPP_BASE = "https://agents.tomba.io" INFLOW_BASE = "https://sandbox.inflowpay.ai" INFLOW_API_KEY = "your_inflow_api_key" def mpp_fetch(endpoint: str) -> dict: """Handle MPP payment flow and return API response.""" url = f"{TOMBA_MPP_BASE}/{endpoint}" resp = httpx.get(url) if resp.status_code != 402: return resp.json() challenge = resp.json()["challenge"] tx = httpx.post( f"{INFLOW_BASE}/v1/transactions/mpp", headers={"X-API-KEY": INFLOW_API_KEY}, json={"challenge": challenge}, ).json() while True: status = httpx.get( f"{INFLOW_BASE}/v1/transactions/{tx['transactionId']}/mpp", headers={"X-API-KEY": INFLOW_API_KEY}, ).json() if status["state"] == "ready": break if status["state"] in ("failed", "expired"): return {"error": f"Payment {status['state']}"} time.sleep(2) return httpx.get( url, headers={"Authorization": f"Payment {status['credential']}"} ).json() @tool("Domain Search") def domain_search(domain: str) -> dict: """Find all email addresses for a company domain. Input: domain name like 'tomba.io'.""" return mpp_fetch(f"domain-search?domain={domain}") @tool("Email Verifier") def email_verifier(email: str) -> dict: """Verify if an email address is valid and deliverable. Input: email address.""" return mpp_fetch(f"email-verifier/{email}") researcher = Agent( role="Lead Research Analyst", goal="Find and verify email contacts for target companies", backstory="You are an expert at finding business contacts using email intelligence tools.", tools=[domain_search, email_verifier], verbose=True, ) task = Task( description="Find all email addresses at tomba.io and verify the top 3 results. Report which emails are valid.", expected_output="A list of verified email addresses with their verification status.", agent=researcher, ) crew = Crew(agents=[researcher], tasks=[task], verbose=True) result = crew.kickoff() print(result)
Vercel AI SDK
Codeimport { openai } from "@ai-sdk/openai"; import { generateText, tool } from "ai"; import { z } from "zod"; import { Mppx, inflow } from "@inflowpayai/mpp-buyer"; const mppx = Mppx.create({ methods: [ inflow({ apiKey: process.env.INFLOW_API_KEY!, environment: "sandbox", }), ], }); const result = await generateText({ model: openai("gpt-4o"), system: "You are a sales research assistant. Use tools to find and verify contacts.", prompt: "Find email addresses at tomba.io and verify the first one", tools: { domainSearch: tool({ description: "Find all email addresses associated with a company domain", parameters: z.object({ domain: z.string().describe("The company domain"), }), execute: async ({ domain }) => { const resp = await mppx.fetch( `https://agents.tomba.io/domain-search?domain=${domain}`, ); return resp.json(); }, }), emailVerifier: tool({ description: "Verify if an email address is valid and deliverable", parameters: z.object({ email: z.string().describe("The email to verify"), }), execute: async ({ email }) => { const resp = await mppx.fetch( `https://agents.tomba.io/email-verifier/${email}`, ); return resp.json(); }, }), emailFinder: tool({ description: "Find a person's email from their name and company domain", parameters: z.object({ domain: z.string(), first_name: z.string(), last_name: z.string(), }), execute: async ({ domain, first_name, last_name }) => { const resp = await mppx.fetch( `https://agents.tomba.io/email-finder?domain=${domain}&first_name=${first_name}&last_name=${last_name}`, ); return resp.json(); }, }), }, maxSteps: 5, }); console.log(result.text);
Best Practices
Error Handling
Always handle MPP payment failures gracefully:
Codeasync function safeMppFetch(url) { try { const response = await mppx.fetch(url); if (!response.ok) { const error = await response.json(); return { error: error.message || "Request failed" }; } return await response.json(); } catch (err) { return { error: `MPP payment failed: ${err.message}` }; } }
Caching Results
MPP charges per request. Cache results to avoid paying for the same data twice:
Codeconst cache = new Map(); async function cachedMppFetch(url) { if (cache.has(url)) { return cache.get(url); } const data = await mppx.fetch(url).then((r) => r.json()); cache.set(url, data); return data; }
Rate Limiting
MPP endpoints are rate-limited to 100 requests per minute. Space out your requests in batch workflows:
Codeasync function batchSearch(domains) { const results = []; for (const domain of domains) { const data = await mppx .fetch(`https://agents.tomba.io/domain-search?domain=${domain}`) .then((r) => r.json()); results.push({ domain, ...data }); // Small delay to stay within rate limits await new Promise((r) => setTimeout(r, 600)); } return results; }
Cost Tracking
Monitor your spending by checking the Payment-Receipt header:
Codeconst response = await mppx.fetch(url); const receipt = response.headers.get("Payment-Receipt"); if (receipt) { // Decode and log the receipt for cost tracking const decoded = JSON.parse(Buffer.from(receipt, "base64url").toString()); console.log(`Paid: ${decoded.reference} | Status: ${decoded.status}`); }
Last modified on