# 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.

```javascript
import 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

```javascript
import 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

```python
from 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

```python
from 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

```typescript
import { 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:

```javascript
async 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:

```javascript
const 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:

```javascript
async 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:

```javascript
const 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}`);
}
```
