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:unsubscribed, from any source. An unsubscribe on one email does not affect the person’s other emails.- Otherwise the newest live result: a real send, bounce, call or hand edit.
- Otherwise the newest check: a verifier’s opinion.
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}, andPOSTorPUTon/public/v2/contacts, takeemail_statuswith three optional keys:email_status_source(the source,apiwhen left out),email_status_checked_at(when the source checked the email, now when left out) andemail_status_live(false when left out).email_status_checked_atneeds a timezone, such as2026-10-10T08:00:00Zor2026-10-10T10:00:00+02:00; a time without one returns a 400. Withemail_status_source,email_statusmust 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 asCatch-Allordeliverableare accepted and stored as theapisource. 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 anapiresult. With any of the three keys the result is written, so{"email_status": "invalid", "email_status_live": true}records a liveapiresult even when a check already showsinvalid. APOSTorPUTwhose body has more than one ofemail,linkedin_urlandphoneignoresemail_statusand the three keys; send the status afterwards withPATCHon the email contact. - MCP.
update_contacttakesemail_status,email_status_sourceandemail_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 emptyemail_statusremoves only that source’s result. - Record page. Setting Email Status by hand writes a live result from the
revyopssource. - Custom fields. A custom field named
email_status,Contact Email StatusorEmail Statusis written as a check from theapisource. Sending the same value again changes nothing while theapiresult still holds it; after the email address changes, the same value is written again.
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 emptyemail_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, undermillionverifier 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 MCPquery_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).
