Industry

What an AI sales agent must know before acting

An AI sales agent will happily write a perfect email to an address that cannot receive it, or queue a call to a number that does not exist. The fix is not a smarter model but a cheap check in front of each action. This post shows which contact signals matter, what our lookups return for them, and how to turn them into a gate your agent cannot skip.

By LookValid data team. Published 2026-10-19.

Check the domain before you write the message

The cheapest failure to prevent is mail to a domain that accepts none. A domain can publish a null MX record, a single MX with preference 0 and a lone dot as the exchange, to say it takes no mail. A domain that publishes it must not publish any other MX record 1. Without an MX record at all, senders fall back to the domain address records and keep retrying for about a week before giving up 1. An agent that writes and schedules a message to such a domain spends tokens and sender reputation for nothing.

Our email domain check reads public DNS for the domain behind an address. It reports MX records, a null MX flag, an implicit MX flag for the address record fallback, the SPF and DMARC records it finds, whether the domain is a known disposable provider or a free mailbox provider, and whether the local part is a role mailbox. It never opens a connection to the mail server, so the field smtp_checked is always false and a good result does not prove that a particular mailbox exists.

Role mailboxes are worth separating from people. RFC 2142 lists the conventional names: info, marketing, sales and support for business functions, abuse, noc and security for network operations, and postmaster, hostmaster and webmaster for services 2. A message to one of them reaches a shared queue, so an agent that writes as if to a named person should route it differently or skip it.

Check the number before you queue the call

The phone lookup parses a number against the numbering plan and returns the E.164 form, national and international formatting, the country, an area code region for North American numbers and a number type such as mobile, fixed line, voip or toll free. The type comes from libphonenumber metadata, which assigns a type whenever feasible and distinguishes fixed line, mobile, toll free, premium rate, shared cost, VoIP and others 3. Its own documentation separates a quick length check from full validation using length and prefix information 3.

This check is about the numbering plan only. It does not contact a carrier, so it cannot say that a number is switched on, ported or reachable, and the response says so in a notes field. Use it to reject numbers that cannot exist and to pick the right channel, not to promise delivery.

Turn signals into gates

A gating table for an outbound agent
SignalValueWhat the agent does next
deliverability_signalundeliverableStop. Do not write or send. Mark the contact for new data.
deliverability_signalriskyHold for a person to review before any send.
disposabletrueSkip the address. Look for another contact at the account.
role_accounttrueSend only a role-appropriate message or route to the account team.
spf.validfalseLog it. A domain can have only one SPF record, and more than one is an error 4.
Phone lookupnot foundDo not dial. The number does not fit the numbering plan.
number_typemobile or fixed_lineChoose the channel that matches the type.
number_typetoll_free, premium_rate or voipHold for review. These types rarely reach a named person.

A gating table for an outbound agent

Two requests give the agent both signals. The bodies below are the ones the API accepts; each costs one call and each returns a small JSON record the gate can read.

curl -X POST https://YOUR_API_HOST/v1/email-domain-check \
  -H "Authorization: Bearer $LOOKVALID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"sales@example.com"}'

Check the domain behind an address

curl -X POST https://YOUR_API_HOST/v1/phone \
  -H "Authorization: Bearer $LOOKVALID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"+14155552671"}'

Check a phone number against the numbering plan

The function below takes the data object from each reply and returns one decision. Keep it in code, not in the prompt, so the model cannot talk its way past it.

type EmailData = { deliverability_signal: 'good' | 'risky' | 'undeliverable'; disposable: boolean; role_account: boolean | null };
type PhoneData = { number_type: string } | null; // null when the lookup found no valid number
​
export function nextStep(email: EmailData, phone: PhoneData): 'send' | 'review' | 'skip' {
  if (email.deliverability_signal === 'undeliverable' || email.disposable) return 'skip';
  if (email.deliverability_signal === 'risky' || email.role_account) return 'review';
  if (phone && ['toll_free', 'premium_rate', 'voip'].includes(phone.number_type)) return 'review';
  return 'send';
}

A gate that reads both results

Put the checks where the agent can call them

  • Call the domain check when a contact enters your system, and again before a sequence starts, since DNS changes.
  • Call the phone lookup once, store the E.164 value, and reuse it.
  • Log every gate decision with the field that caused it, so you can explain a skipped contact later.

The same lookups are available to AI clients as tools over MCP, which suits an agent that decides for itself when to check. Start with the quickstart, then add the email domain check in front of your first send.

Sources

  1. RFC 7505: A "Null MX" No Service Resource Record for Domains That Accept No Mail, read 2026-10-19
  2. RFC 2142: Mailbox Names for Common Services, Roles and Functions, read 2026-10-19
  3. libphonenumber: the Google library and metadata behind phone number types, read 2026-10-19
  4. RFC 7208: Sender Policy Framework (SPF), read 2026-10-19