URL: /code/data/people/verify-email · Id: `page:/code/data/people/verify-email`

# ContactPoint.verifyEmail

> Check addresses you already hold: does the mailbox exist, does the domain accept everything, and is it a person or a role.

`POST https://api.sb/{your-domain}/contact-points/verify-email`

Check addresses you already hold: does the mailbox exist, does the domain accept everything, and is it a person or a role.

- **Price:** 18¢ per 100 addresses
- **Success rate:** 99.99% of 2,098,997 calls
- **Freshness:** Fresh for 30 days
- **Emits:** `ContactPoint.verified`
- **Capability:** [Enrichment](https://startupbuilder.dev/systems/sales/enrichment)

```text title="Run it · Ask"
Are dana@harborview-pm.com and leasing@crestline-residential.com safe to send to? Spend up to 1¢.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.ContactPoint.verifyEmail({ addresses: ['dana@harborview-pm.com', 'leasing@crestline-residential.com'], maxCost: 0.01 })
```

```json title="Run it · Response"
{
  "results": [
    {
      "address": "dana@harborview-pm.com",
      "result": "deliverable",
      "catchAll": false,
      "role": false,
      "disposable": false,
      "freeMail": false,
      "suggestion": null,
      "observedAt": "2026-10-27T14:02:11Z",
      "method": "sampled"
    },
    {
      "address": "leasing@crestline-residential.com",
      "result": "catch-all",
      "catchAll": true,
      "role": true,
      "disposable": false,
      "freeMail": false,
      "suggestion": null,
      "observedAt": "2026-10-27T14:02:11Z",
      "method": "sampled"
    }
  ],
  "cost": {
    "usd": 0,
    "maxUsd": 0.0036,
    "units": 1,
    "misses": 0
  }
}
```

## Options and prices
| You get | Input that selects it | Price | Success rate |
| --- | --- | --- | --- |
| Check the mailbox | `{"depth":"standard"}` | About 18¢ per 100 addresses | 99.99% of 2,098,997 calls |
| A second opinion on a catch-all | `{"depth":"deep"}` | About 0.4¢ an address | 100% of 1,187,863 calls |

## Inputs
| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `addresses` | `string[]` | Required | 1 to 100 addresses, checked in parallel. |
| `depth` | `'standard' \| 'deep'` | `'standard'` | `deep` asks a second checker that resolves many catch-all domains to `deliverable` or `invalid`; it is billed per address. |
| `timeoutSeconds` | `number` | `20` | How long to wait on a slow mail server, 2 to 60. A server that never answers reads `unknown`, at $0. |
| `maxCost` | `number` | `0.50` | The most this call may spend, in USD. |

## Outputs
| Field | Type | What `null` means |
| --- | --- | --- |
| `results` | `ContactPoint[]` | Never null: one row per address sent, in order. |
| `results[].address` | `string` | Never null. |
| `results[].result` | [`EmailResult`](/code/data/types#emailresult) | Never null: `unknown` when the mail server did not answer. |
| `results[].catchAll` | `boolean \| null` | The server did not say whether it accepts every address. |
| `results[].role` | `boolean \| null` | Unknown. `true` is a shared inbox such as leasing@. |
| `results[].disposable` | `boolean \| null` | Unknown. |
| `results[].freeMail` | `boolean \| null` | Unknown. `true` is a personal mailbox provider, not a company domain. |
| `results[].suggestion` | `string \| null` | No likely typo: `gmail.com` is suggested for `gmial.com`. |
| `results[]` | [`Observed`](/code/data/types#observed) | Each row also carries `observedAt` and `method`. |
| `cost` | [`Cost`](/code/data/types#cost), in USD | Never null. In try mode `usd` is 0 and `maxUsd` is what the call would have cost. |

## Read the result
| Field | Value | What it means | What to do |
| --- | --- | --- | --- |
| `result` | `deliverable` | The mailbox exists and accepts mail. | Send. (the send rule for cold email) |
| `result` | `catch-all` | The domain accepts every address, so this one cannot be confirmed. | Run `depth: deep`, or send it in its own batch capped at 20% of the day’s sends. (our planning number for cold email) |
| `result` | `unknown` | The mail server never answered; it cost nothing. | Check again with `depth: deep` before you send. (the free-miss rule) |
| `result` | `invalid` or `disposable` | The mailbox does not exist, or it is a throwaway. | Suppress it; never send. (the send rule for cold email) |
| `role` | `true` | A shared inbox, not a person. | Drop it from cold email; it can go to an inbound reply. (the list rule for cold email) |
| `result` | Hard bounces over 2% of a batch | The checks are stale or catch-alls are too heavy in the mix. | Stop the batch, re-check every address, and cap catch-alls. (the 2% bounce line, our planning number) |

## Effects
**Action class**

None: it reads, so no approval rule names it

**Modes**

- `try`: Runs the read and returns real rows; nothing is saved and nothing is billed.
- `send`: Returns a pending Action at once; its Event carries the rows when the read finishes.
- `do`: Waits for the read and returns the rows, billed per result.

**Idempotency**

The same `Idempotency-Key` with the same input replays the first answer at $0; with other input it returns 409.

**Saved or shared**

Nothing is written to your records. Readings about people are never shared across startups.

**Emits**

`ContactPoint.verified` with `{ results }`

**Performed by**

[Prospector](https://startupbuilder.dev/agents/sales/prospector)

**Price**

About 18¢ per 100 addresses. Free: catch-all and unknown results at the standard depth. `maxCost` defaults to 50¢. A list of 400 addresses checked: 400 × 0.18¢ an address is about 71¢.

**Reliability**

The first route succeeds 99.99% of 2,098,997 calls. If it fails, the call refuses with `sources-busy` and nothing is spent.

**Freshness**

A reading younger than 30 days returns at $0. Readings about people are never shared across startups.

**Batch and pages**

Up to 100 addresses a call. It answers in one response.

**Rate**

[The shared rate rules](/code/data#rate): 10 calls a second per key, and an array input counts as one call.

## Refuses when
This call has no refusals of its own. Every data call can also meet [the shared data-call refusals](/code/refusals#data-calls): [`filter-unsupported`](/code/refusals#filter-unsupported), [`input-missing`](/code/refusals#input-missing), [`limit-exceeded`](/code/refusals#limit-exceeded), [`max-cost`](/code/refusals#max-cost), [`rate-limited`](/code/refusals#rate-limited), [`sources-busy`](/code/refusals#sources-busy), [`venue-unsupported`](/code/refusals#venue-unsupported).

## When it waits
Returns in one response; nothing waits.

## Example

**Ask**

```text
Are dana@harborview-pm.com and leasing@crestline-residential.com safe to
send to? Spend up to 1¢.
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/contact-points/verify-email?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"addresses":["dana@harborview-pm.com","leasing@crestline-residential.com"],"maxCost":0.01}'
```

The response is the one in the Run it window above.

**CLI**

```bash
npx startup-builder do ContactPoint.verifyEmail --try --data '{"addresses":["dana@harborview-pm.com","leasing@crestline-residential.com"],"maxCost":0.01}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.ContactPoint.verifyEmail({ addresses: ['dana@harborview-pm.com', 'leasing@crestline-residential.com'], maxCost: 0.01 })"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const result = await $.try.ContactPoint.verifyEmail({
  addresses: ['dana@harborview-pm.com', 'leasing@crestline-residential.com'],
  maxCost: 0.01,
})
```

To retry, send the same `Idempotency-Key`: the retry returns the first answer for $0. A new key reads again.

A `sk_test_` key answers these the same way every time, at $0 ([every test value](/code/data#test-values)).

| Test value | Always returns | What it is for |
| --- | --- | --- |
| `dana@harborview-pm.com` | `{"result":"deliverable","catchAll":false,"role":false}` | Always deliverable: a person’s own mailbox that accepts mail. |
| `leasing@crestline-residential.com` | `{"result":"catch-all","catchAll":true,"role":true}` | Always catch-all: the domain accepts every address, so the check cannot confirm this one, and it costs nothing. |
| `former.manager@harborview-pm.com` | `{"result":"invalid","catchAll":false,"role":false}` | Always invalid: the mailbox does not exist, as when someone has left the company. |
| `office@quietpine-rentals.test` | `{"result":"unknown","catchAll":null,"role":true}` | Always unknown: the mail server never answered, so check again with depth deep; it costs nothing. |
| `maxCost: 0.0001` | Refuses with [`max-cost`](/code/refusals#max-cost) | Any priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap. |
| `rate-limited.test` | Refuses with [`rate-limited`](/code/refusals#rate-limited) | Any call naming this hostname answers 429 with Retry-After, so your retry logic runs. |
| `sources-busy.test` | Refuses with [`sources-busy`](/code/refusals#sources-busy) | Any call naming this hostname answers 503 as if every route were busy, and nothing is spent. |

## Used by
- **Workflows:** [Build a verified list](/capabilities/find-customers/build-a-verified-list) · [List refresh](/capabilities/find-customers/list-refresh) · [Pick who to sell to first](/capabilities/find-customers/pick-who-to-sell-to)
- **Agent tasks:** [Link pitches](/capabilities/earn-first-links/link-pitches)
- **Services:** [Verified list with a bounce warranty](/capabilities/find-customers/verified-list)
- **Jobs:** [Find your first customers](/capabilities/find-customers) · [Reach out](/capabilities/reach-out)

## Related
[`Prospect.count`](/code/data/people/count) · [`Prospect.find`](/code/data/people/find) · [`Prospect.verify`](/code/data/people/verify) · [`ContactPoint.findEmail`](/code/data/people/find-email) · [`ContactPoint.findPhone`](/code/data/people/find-phone) · [`Contact.enrich`](/code/data/people/enrich)