Skip to main content
An email can be checked by several tools over time: Million Verifier says catchall, BounceBan later says valid, a sequencer reports a bounce, a rep corrects it by hand. RevyOps keeps one result per source for each email, with its own dates, and shows one of them as the contact’s Email Status.

Which result shows

Email Status shows, in this order:
  1. unsubscribed, from any source. An unsubscribe on one email does not affect the person’s other emails.
  2. Otherwise the newest live result: a real send, bounce, call or hand edit.
  3. Otherwise the newest check: a verifier’s opinion.
An email with no results keeps the Email Status it already had. If that status is bounced or unsubscribed, the first new result first saves it as a live result from the before_store source, dated just before that result. A verifier check never hides it; a newer live result, such as a hand edit, replaces a bounced one, and unsubscribed always wins. before_store cannot be named as a source, to write or to clear. Any other status from before is replaced by the first new result. Results arrive when someone writes them: a hand edit in the app, or a call to the API or MCP that names the source, such as your verification script naming millionverifier or a workflow naming instantly. Integrations do not write results by themselves yet. For jo@acme.com:

Values

Every source writes one of seven values: valid, invalid, risky, catchall, unknown, bounced and unsubscribed. A tool’s own word, such as ok or accept_all, is mapped to one of them before it is written; the word itself is kept beside the result as raw_value. bounced is any bounce, hard or soft. A bounce is always live, so it outranks every later verifier check until a send, a hand edit or another live result replaces it. bounced and unsubscribed are always stored as live, whatever the writer sends.

The two dates

Both come from the source. A result dated earlier than the one the same source already has is ignored, without an error, so a retried webhook cannot undo a newer check.

Sources

A source is either one RevyOps knows (millionverifier, bounceban, leadmagic, apollo, the sequencer and CRM integrations, revyops for edits on the record page, api for a write that names no source), or one the workspace adds itself. To write results from a tool RevyOps does not list, add it once as a source under Activities & Scoring or with the create_source MCP tool, for example with the key acmeverify. The integration names and api cannot be added as a workspace’s own source. revyops is kept for hand edits on the record page: the API and MCP cannot name it as a source, and doing so returns a 400. A clear without a source still removes every result, the hand edit included.

Writing a status

  • Contacts API. PATCH /public/v2/contacts/{contact_id}, and POST or PUT on /public/v2/contacts, take email_status with three optional keys: email_status_source (the source, api when left out), email_status_checked_at (when the source checked the email, now when left out) and email_status_live (false when left out). email_status_checked_at needs a timezone, such as 2026-10-10T08:00:00Z or 2026-10-10T10:00:00+02:00; a time without one returns a 400. With email_status_source, email_status must be one of the seven values and any other word returns a 400 that lists them. Without it, the older behavior stays: any letter case and verifier spellings such as Catch-All or deliverable are accepted and stored as the api source. Sending back the value Email Status already shows, with none of the three keys, changes nothing, so reading a contact and writing it back unchanged does not add an api result. With any of the three keys the result is written, so {"email_status": "invalid", "email_status_live": true} records a live api result even when a check already shows invalid. A POST or PUT whose body has more than one of email, linkedin_url and phone ignores email_status and the three keys; send the status afterwards with PATCH on the email contact.
  • MCP. update_contact takes email_status, email_status_source and email_status_checked_at; the source and the check time, with its timezone, are required whenever a status is sent, because the time has to come from the tool. An empty email_status removes only that source’s result.
  • Record page. Setting Email Status by hand writes a live result from the revyops source.
  • Custom fields. A custom field named email_status, Contact Email Status or Email Status is written as a check from the api source. Sending the same value again changes nothing while the api result still holds it; after the email address changes, the same value is written again.
Only a contact with an email address takes an email status. On a LinkedIn or phone contact the custom field above is ignored, and so is an email_status sent without email_status_source; with a source the request returns a 400.

Removing results

Statuses from before per-source results

A contact whose Email Status was set before per-source results existed has an empty email_statuses list: there is no record of which tool set it. The list fills as sources write. Only bounced and unsubscribed are kept as a before_store result when the first new result arrives, as described under Which result shows; a before_store entry then appears in the list like any other source.

Reading results

GET /public/people/{person_id} returns a statuses list on every item in emails, and GET /public/v2/contacts/{contact_id} returns the same list as email_statuses. The app’s person and contact pages show the same list beside Email Status, where before_store reads as Already on record; see Seeing each tool’s result. Each entry has source, status, raw_value, live, observed_at and status_changed_at, newest check first. The MCP get_contact tool returns the same list.

Phone statuses

A workflow’s Verify email step writes results the same way, under millionverifier or bounceban, through the workspace’s status mapping. Phones get a status too, from a workflow’s Verify phone step, which checks them with Twilio. Each check is kept per source like an email result, and the contact shows the newest one as Phone Status (valid, unreachable, invalid or unknown) and Phone Line Type (mobile, landline, voip or other). Both are read-only, show on the phone’s row of the person and contact pages with who checked it and when, and can be filtered on in the People and Contact lists.

Filtering by result

The People and Contact lists filter by each tool’s own result through the Email Status Result group; see Filtering by each tool’s email status. In the filter API and the MCP query_entity and save_view tools, these results are the email_status_result entity, reached from a contact or person through the email_status_results relation, with the fields source, status, raw_value, live, observed_at and changed_at (the result’s status_changed_at).