# Contact verification for AI agents

> Let AI agents and MCP tools check a phone number or e-mail address before they send, call or book, with scoped agent keys, spend caps and confirmation.

Canonical: https://mobilevalidate.com/use-cases/ai-agents-contact-verification · Last updated: 2026-09-29

![An AI agent dashboard lists tool calls with status codes, active guardrails, a spend cap meter and a confirmation dialog before an action runs.](https://mobilevalidate.com/media/library/ai-agent-guardrails-dashboard-1600.webp)

*Tool calls, guardrails, spend caps and human confirmation for autonomous agents.*


AI agents should verify a phone number or e-mail address before they send a message, place a call or book something. One MobileValidate call tells the agent whether the number is live on its network, which line type it is and whether the mailbox exists. Agent keys, spend caps and a confirmation step keep the agent's spending bounded.

## Why do AI agents need contact verification?

An agent that acts in the world acts on contact details it didn't collect itself. A user dictates a number, a support ticket contains an address, a spreadsheet row has a phone in a national format. People make typos, and documents go out of date. A human assistant would notice that a number looks short or that an address domain is misspelled. An agent often won't, unless it has a tool that tells it.

The cost of acting on a bad contact is real:

- **Failed actions.** An SMS to an unassigned number or an e-mail to a mailbox that doesn't exist fails. The agent may then report success because the send call itself returned OK.
- **Wrong recipients.** A reassigned or mistyped number reaches a stranger. For an agent that sends reminders or payment links, that is a privacy incident.
- **Wasted spend.** Every failed SMS, call minute or retry costs money, and agents retry faster than people do.
- **Bad downstream records.** An agent that writes to a CRM spreads the error to every later workflow.

A verification tool gives the agent a fact it can reason about: *the number is reachable*, *the mailbox doesn't exist*, *this is a fixed line, so SMS won't work*. The agent can then correct itself or ask the user before it acts.

## How does it work?

1. **Normalize first.** The agent calls `normalize_numbers` (free, runs locally) to turn what it has into [E.164](/glossary/e164) format and flag ambiguous inputs, such as a national number with no country.
2. **Estimate.** For anything beyond a handful of contacts, the agent calls `estimate_cost`. It's free and returns valid, invalid, duplicate and cached counts plus the maximum cost.
3. **Check.** The agent calls `lookup_numbers` or `lookup_emails` with the checks it needs, for example `hlr` and `email`. Behind the tools sits the same `POST /v1/lookup` endpoint your own code would use.
4. **Confirm if asked.** If the maximum cost is above the confirmation threshold, the server answers `confirmation_required` with the amount. The agent shows that amount to the user and calls again with `confirm_max_cost`.
5. **Decide.** The agent applies simple rules: act, ask the user to correct, or pick another channel. Unknown means "no information", never "bad contact".
6. **Record.** The agent stores the decision and `checked_at` with the action, not the whole response.

The [MCP server](/docs/mcp) runs hosted at `https://mcp.mobilevalidate.com/mcp` or locally with the `@mobilevalidate/mcp` package. Agents that don't speak the [Model Context Protocol](/glossary/model-context-protocol-mcp) can call the REST API directly with an agent key.

## Which checks should an agent use?

| Check | What it answers | When the agent should use it | Service |
|---|---|---|---|
| `hlr` | Is the mobile number assigned and reachable right now? Which network serves it? | Before sending an SMS, a code or a payment link | [HLR lookup](/services/hlr-lookup) |
| `carrier` | Is it mobile, fixed line or VoIP, and on which carrier? | Before choosing between SMS and a call | [Carrier lookup](/services/carrier-lookup) |
| `mnp` | Was the number ported, and which network is it on now? | When the agent routes messages or compares with a stored network | [MNP lookup](/services/mnp-lookup) |
| `email` | Does the mailbox exist at a major webmail provider? | Before sending a confirmation, invoice or booking by e-mail | [E-mail mailbox check](/services/email-verification) |
| `whatsapp` | Does the number have a WhatsApp account? | When the user has opted in to messages on WhatsApp | [WhatsApp check](/services/whatsapp-number-check) |
| `spam` | Does the number appear in spam and nuisance-call reports? | Before an agent calls back or trusts an inbound number (limited access) | [Spam reputation](/services/spam-reputation) |

Keep the tool list small. Give the agent only the checks its task needs, and issue the agent key with only the scopes those tools need. An agent that books appointments needs `hlr` and `email`, not every check in the catalog. `list_services` shows which checks the key can use, with prices.

## What guardrails keep an agent's spending safe?

Agents can loop, retry and misread instructions. The MCP server assumes that and puts limits outside the model:

- **Agent keys only.** Live keys (`mv_live_…`) are rejected by the MCP server. An agent key (`mv_agent_…`) has exactly the [scopes](/docs/authentication) it was issued with and a daily spend cap.
- **Confirmation above a threshold.** Spending calls whose maximum cost is above the threshold (default USD 1.00), and jobs with more than 100 numbers and e-mails, are refused with `confirmation_required`. The confirmed amount is then sent to the API as `max_cost`, so the request can't cost more.
- **Hard caps you control.** The confirmation goes through the agent, so the server can't prove a human approved it. The daily spend cap and the scopes are the real limits. Set them to what the task needs.
- **Anti-enumeration.** 20 or more consecutive numbers, or generated e-mail lists, are refused with `suspected_enumeration`, just as in the API.
- **No free-text echo.** Tool output never echoes request metadata, and tools have no webhook URL parameter, which limits what a prompt injection can make the agent send out.

The MCP specification itself says that for trust and safety there should always be a human in the loop with the ability to deny tool invocations ([MCP specification, tools](https://modelcontextprotocol.io/specification/2025-06-18/server/tools)). Spending confirmations are one practical way to follow that advice.

## Example request

With a test key, `+447700900001` answers `reachable` and `unknown@test.mobilevalidate.com` answers `unknown`. See [test values](/docs/test-values). The agent sets `max_cost`, so the call can never cost more than it agreed to:

```bash
curl https://api.mobilevalidate.com/v1/lookup \
  -H "Authorization: Bearer $MOBILEVALIDATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"numbers": ["+447700900001"], "emails": ["unknown@test.mobilevalidate.com"], "checks": ["hlr", "email"], "max_cost": {"amount": "0.02", "currency": "USD"}}'
```

Response (excerpt, test mode: the `checks` of the phone row, then of the e-mail row):

```json
[
  {
    "number.hlr": {
      "service": "number.hlr", "status": "completed", "registered": true,
      "attributes": {"status": "reachable", "ported": false, "roaming": false, "network": "Test Mobile", "mcc_mnc": "23415", "country": "GB"},
      "confidence": "high", "confidence_score": 0.99, "checked_at": "2026-09-28T22:40:26.550Z",
      "cached": false, "age_seconds": 0, "billed": false, "reason": null, "poll_after_ms": null
    }
  },
  {
    "email.valid": {
      "service": "email.valid", "status": "unknown", "registered": null, "attributes": null,
      "confidence": null, "confidence_score": null, "checked_at": null,
      "cached": false, "age_seconds": null, "billed": false, "reason": "UPSTREAM_TIMEOUT", "poll_after_ms": null
    }
  }
]
```

The number is reachable, so the agent can send the SMS. The mailbox answer is unknown and free, so the agent should neither trust nor reject the address. It can send the e-mail as usual or ask the user to confirm it. Through MCP, the same call is `lookup_numbers` with `numbers`, `emails` and `checks`.

## What should the agent do with each result?

| Result | Agent action |
|---|---|
| HLR `status: reachable` | Proceed with SMS or a call |
| HLR `status: unreachable` | Don't wait for an SMS to arrive. Offer e-mail or try later |
| HLR `status: invalid` or `number_status: invalid_number` | Stop. Ask the user to check the number |
| `line_type: fixed_line` | Don't send SMS. Call or use e-mail instead |
| Mailbox `registered: false` | Stop. Ask the user to check the address |
| Any check `unknown` | Continue with normal handling or ask the user. Never treat it as a failure |
| `confirmation_required` | Show the amount to the user and wait for an explicit yes |

Write these rules into the agent's instructions or tool descriptions. Agents follow short, explicit rules better than general advice to "be careful".

## How much does it cost?

Each check is billed per identifier, and only when the answer is conclusive. You're not charged for inconclusive results (unknown, unsupported country, timeout, invalid, duplicate). The HLR lookup is $0.005 per number and the MNP lookup is $0.001 per number. See [pricing](/pricing) for every other check.

For agents, per-call cost is usually small, because an agent checks one or two contacts at a time. The risk is volume: a loop that checks the same list again and again. Answers inside each service's freshness window come from your account's cache and are free (`cached: true, billed: false`), which blunts that risk. The daily spend cap stops the rest.

## What about consent and acceptable use?

The agent should check only contacts it has a lawful reason to process: the user's own contacts, customers, people who asked to be contacted. A passed check is not consent to message someone. Business messages on WhatsApp and similar platforms require the recipient's opt-in.

The [acceptable use policy](/legal/acceptable-use) applies to agents exactly as to people. It forbids unsolicited bulk messaging, scraping, and building lists of the people who use an app. Checks never return names or profiles, and reverse lookups are not offered. People can object to checks of their number or address through the [opt-out form](/opt-out). Suppressed contacts are never checked or charged.

## What are common mistakes?

- **Giving the agent a live key.** Use an agent key with a daily cap. The MCP server refuses live keys for this reason.
- **Treating unknown as fake.** An agent that tells a user "your number is invalid" after an unknown answer is wrong and loses trust.
- **Checking after acting.** The check belongs before the send, call or booking, not in a report afterwards.
- **Letting the agent confirm its own spending.** Route `confirmation_required` to a person, or set the threshold and cap so that no confirmation is ever needed for routine work.
- **Storing full responses in memory.** Keep the decision and `checked_at`. Results describe a person's phone, so keep only what the task needs.
- **Too many tools.** Every extra check is another way for the agent to spend. Match the key's scopes to the task.

## Frequently asked questions

### Why should an AI agent verify a phone number or e-mail before acting?

Because agents act on contact details that users typed, pasted or that came from documents, and a wrong digit or a dead mailbox turns into a failed message, a wasted call or a message to the wrong person. A check first gives the agent a fact to reason with instead of a guess.

### Can an agent use my live API key?

No. The MCP server accepts only agent keys (mv_agent_…) and test keys. Agent keys carry only the scopes they were issued with and a daily spend cap, so an agent can never spend without a limit.

### What stops an agent from running up a large bill?

Three layers. Calls whose maximum cost is above the confirmation threshold (default USD 1.00), or jobs with more than 100 identifiers, are refused until the agent repeats them with the confirmed amount. That amount is enforced as max_cost. The agent key's daily spend cap is the hard limit.

### Does the agent get a name or profile for the number?

No. Tools return yes, no or unknown per check, plus network data such as line type, carrier or reachability. Names, photos and profiles are never returned, and reverse lookups are not offered.

### What should the agent do with an unknown result?

Treat it as missing information, not as a negative answer. Unknown results are free. The agent should fall back to its normal behaviour or ask the user, never conclude that the contact is fake.

### Can an agent search for people who use a messaging app?

No. Checks run only on numbers and addresses the agent already has for a lawful reason. Sequential number ranges and generated e-mail lists are rejected, and the acceptable use policy forbids building lists of app users.

### Can I try it without spending anything?

Yes. Test keys answer the documented test numbers and addresses, such as +447700900001 and registered@test.mobilevalidate.com, with fixed answers and are never billed.
