# VerifyGenius: Clean contact data, starting with email

> VerifyGenius tells you which of the contact data you already hold is safe to use, starting with email. It asks the company’s own mail server whether a business address is real, then hangs up. No email is ever sent, and only addresses we can prove are called safe to send.

Built for UK business domains. Email today; phone numbers and postal addresses are next. Private for now, running for our own products.

Operator: Rajoka Limited, UK company 12069067, 64b Yardley Green Road, Birmingham B9 5QE. Contact: postmaster@verifygenius.com. Site: https://verifygenius.com

## The six results

| Result | What it means | Safe to send? |
|---|---|---|
| `valid` | The server refused a made-up address and accepted this one, on the same machine. | Yes, for 30 days |
| `invalid` | The server said there is no such user, or the domain has no mail server at all. | No |
| `catch_all` | The server accepts any address, real or not, so its “yes” proves nothing. | No |
| `unknown` | No clear answer: greylisting, a policy reply, an unreachable server, or a reply we haven’t calibrated. | No |
| `suppressed` | The address, company or domain has opted out. We didn’t check it and won’t. | No |
| `bad_syntax` | Not an email address, so nothing was checked. | No |

## What happens in one check

1. **Opt-outs first.** If the address, its company or its domain (parent domains included) has opted out, we stop here and return `suppressed`. No connection is made.
2. **Find the mail server.** We look up the domain’s MX records. No mail server means `invalid`; a DNS failure means `unknown`, never a guess.
3. **Ask about someone who doesn’t exist.** A made-up address goes first, the same one for that domain all month, so greylisting can clear on the retry. If the server accepts it, the domain is `catch_all` and we stop.
4. **Ask about the real address.** The answer only counts if it comes from the same server that refused the made-up one. A different server answering is `unknown`.
5. **Read the reply with calibrated rules.** Only a clear “no such user” is `invalid`. Anything naming a policy, our IP or the sender stays `unknown`. Microsoft 365’s `550 5.4.1` counts only when another address at the same company has been accepted, because Microsoft also uses it for faults that affect a whole tenant.
6. **Hang up.** `QUIT` before `DATA`, every time. A greylisted server gets one more try, 20 minutes later. Then we move on.

## Evidence

We tried to fool it 530 times. It never said yes.

- **0 of 152** addresses gave a different answer when asked again hours later. A day later, 100 of 100 matched.
- **149 of 207** addresses printed on companies’ own websites came back valid. 3 had gone stale, and those are the ones that bounce.
- **0 refusals** because of our IP address. It isn’t on Spamhaus, SpamCop, Barracuda, PSBL, Mailspike or UCEPROTECT.
- **22% undecided** of domains, on purpose. It was 67% before the reply rules, and 16% outside Microsoft 365. We’d rather tell you “unknown” than guess.

Method: 200 UK companies with a website proven by their company number, about 1,030 checks from the production IP address across four passes, 27 September 2026, plus a repeat the next day. Each company got a made-up address and up to three addresses published on its own site.

## API v1

JSON over HTTPS, one bearer key per product. The same API runs our own products today. Early-access keys come with your base URL; examples use `$VG_BASE`.

### Quick start
```sh
curl -s "$VG_BASE/v1/check" \
  -H "Authorization: Bearer $VG_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane.cole@acme-dental.example"}'
```
Response `200`:
```json
{
  "email": "jane.cole@acme-dental.example",
  "status": "valid",
  "reason": null,
  "smtp_code": 250,
  "smtp_text": "2.1.5 OK",
  "mx_host": "mx.acme-dental.example",
  "checked_at": "2026-09-28 11:02:13.482117+00:00",
  "job_id": "5b0e7d0c-3f6a-4c55-9a1e-0f3c2d8b7a14"
}
```
### Keys
Send `Authorization: Bearer vg_…` on every request except health and the OpenAPI document. Each product gets its own key. We store only a SHA-256 hash, so a key is shown once, when it is issued; if you lose it, we revoke it and issue another. Jobs are private to the key that created them.
### POST /v1/check
Checks one address and waits up to 60 seconds for the answer. Most answers take a few seconds. A slow server or greylisting returns `202` with the item still `pending` or `checking`; keep the `job_id` and read it later.
```json
{ "email": "jane.cole@acme-dental.example" }
```
### POST /v1/jobs
Queues up to 5,000 addresses and returns at once. Addresses that aren’t addresses come back as `bad_syntax` without a check.
```json
{ "emails": ["jane.cole@acme-dental.example", "accounts@acme-dental.example"] }
```
### GET /v1/jobs/{id}
The job and its results so far, up to 1,000 per page: pass `offset` and `limit`, and follow `next_offset` (null on the last page). `done` counts finished items across the whole job; poll until it equals `total`. A job created with a different key returns `404`.
```json
{
  "id": "9d2f4b61-8c0e-4e7a-b3d5-2a6f1c9e0b87",
  "requested_by": "your-product",
  "total": 2,
  "done": 2,
  "created_at": "2026-09-28 11:04:50.017342+00:00",
  "items": [
    { "email": "jane.cole@acme-dental.example", "status": "valid", "reason": null,
      "smtp_code": 250, "smtp_text": "2.1.5 OK", "mx_host": "mx.acme-dental.example",
      "checked_at": "2026-09-28 11:05:02.311904+00:00" },
    { "email": "accounts@acme-dental.example", "status": "invalid", "reason": null,
      "smtp_code": 550, "smtp_text": "5.1.1 User unknown", "mx_host": "mx.acme-dental.example",
      "checked_at": "2026-09-28 11:05:06.908215+00:00" }
  ]
}
```
### Result fields
- `status`: `pending` · `checking` · `valid` · `invalid` · `catch_all` · `unknown` · `suppressed` · `bad_syntax`. Only `valid` is sendable, for 30 days from `checked_at`.
- `reason`: Why, when the status alone doesn’t say: `domain accepts any address`, `no_mx`, `mail server unreachable`, `answered by a different MX than the probe`, `microsoft 5.4.1 on every address: not trusted`, `catch-all undecided (451)`. Otherwise `null`.
- `smtp_code`, `smtp_text`: The server’s own reply to `RCPT TO`, so you can see the evidence rather than take our word for it.
- `mx_host`: The mail server that answered.
- `checked_at`: When the answer came back (UTC). Empty while pending.
- `flags`: `role` (info@, sales@…), `free_mail` (Gmail, Outlook…), `disposable` (throwaway domains, from a public list) and `plus_address` (`name+tag@`). Worked out from the address alone; a flag never changes the verdict.
- `suggestion`: A likely intended address when the domain looks like a typo of a common provider, such as `jane@gmial.com` → `jane@gmail.com`. Otherwise `null`.
- `score`, `reasons`: 0–100 for sorting, never a second opinion: `valid` 95, `catch_all` 45, `unknown` 25, everything else 0; a role address costs 5, a disposable domain caps it at 10. `reasons` lists every rule that applied, e.g. `["status_valid", "role_address"]`. Empty while pending.
### Errors
- `400`: Bad JSON, no addresses, or more than 5,000 in one job, or a bad `offset`/`limit`. The body says which.
- `401`: Missing or revoked key: `{"error": "a valid key is required (Authorization: Bearer vg_...)"}`
- `404`: No such job for your key, or no such route.
- `500`: Our fault. Safe to retry.
### Limits
Checks leave from one IP address with a published name, at up to 20 a minute, one conversation per domain at a time. A circuit breaker pauses everything if servers start refusing us. That pace keeps us welcome on the servers you want answers from. Hand-started checks jump the queue ahead of background work.
### Status and health
`/v1/status` returns queue counts, checks in the last hour, and whether the sending IP appears on any of eight blocklists. `/v1/health` needs no key. `/v1/openapi.json` is the machine-readable version of this page.

## For mail server administrators

If `api.verifygenius.com` (`169.58.33.41`) connected to your server, that was a mailbox check, not an email. It asked whether one business address exists, then disconnected.

- **It never sends a message.** EHLO, MAIL FROM, RCPT TO, QUIT. The conversation ends before `DATA`.
- **It is slow on purpose.** One conversation per domain at a time, 20 checks a minute in total, and a pause whenever servers push back.
- **It says who it is.** Reverse DNS, HELO and SPF all name `api.verifygenius.com`. The sender is `verify@verifygenius.com`.
- **It checks business addresses.** Addresses published on UK companies’ own websites, or built from the names of a company’s registered directors, so later business emails don’t bounce.
- **Stop checks for your domain.** Opt out at https://insightbase.com/opt-out or email postmaster@verifygenius.com. Opt-outs are applied before any further check.

## Questions

### Can I use it today?
Not yet. VerifyGenius runs privately for Rajoka’s own products, including InsightBase and Certivus. We’re gauging interest before opening it up. Tell us your volume and use below and we’ll get in touch.

### Is it only for email?
Email is first. VerifyGenius is a data sanitisation service: it checks contact data you already have, without contacting anyone. We plan to add phone numbers and postal addresses, with the same rule of showing the evidence behind every answer. Tell us in the form which you need.

### What will it cost?
We haven’t set prices. Your answer on monthly volume helps us set them. Nobody is charged for joining the early-access list.

### Why is so much “unknown”?
Because some servers won’t give a straight answer, and guessing is how you end up bouncing. Microsoft 365 is the main one: when a company’s tenant rejects every address, we can’t tell a missing mailbox from a tenant-wide block, so we say `unknown`. Outside Microsoft 365, 16% of domains stayed undecided in our tests.

### Why isn’t catch-all “valid”?
A catch-all server says yes to anything, including addresses we made up a second earlier. That yes carries no information about the address you asked for, so we won’t call it safe to send.

### Does it work on Gmail or personal addresses?
It is built and calibrated for business domains, mostly in the UK. Big free-mail providers often don’t answer mailbox questions honestly, and we don’t claim results we haven’t tested.

### Where does it run?
On servers we run ourselves. Checks leave from a single IP address, `169.58.33.41`, with its own reverse DNS, and nothing about a check is shared with anyone else.
