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

# Prospect.verify

> A Workflow over three calls: find a saved Prospect’s work email, check the mailbox, and write the result onto the Prospect; a miss is free.

`POST https://api.sb/{your-domain}/prospects/verify`

A [Workflow](https://startupbuilder.dev/business/nouns/workflow) over three calls: find a saved [Prospect](https://startupbuilder.dev/business/nouns/prospect)’s work email, check the mailbox, and write the result onto the Prospect; a miss is free.

- **Price:** 0.6¢ a verified address
- **Success rate:** 94.9% across its steps
- **Freshness:** Fresh for 90 days
- **Emits:** `Prospect.verified`
- **Capability:** [Enrichment](https://startupbuilder.dev/systems/sales/enrichment)

```text title="Run it · Ask"
Find and check Dana Whitfield’s work email before I write to her; spend up to 2¢.
```

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

const result = await $.try.Prospect.verify({ prospect: 'prospect_AhvINObofR', maxCost: 0.02 })
```

```json title="Run it · Response"
{
  "prospect": "prospect_AhvINObofR",
  "emails": [
    {
      "address": "dana@harborview-pm.com",
      "result": "deliverable",
      "checkedAt": "2026-10-27T14:09:03Z"
    }
  ],
  "phones": [],
  "reachable": true,
  "cost": {
    "usd": 0,
    "maxUsd": 0.02,
    "units": 1,
    "misses": 0
  }
}
```

## How it runs
One call for a saved Prospect that runs these Data Verbs in order and writes what they find onto the Prospect. Call the steps yourself to hold the addresses without writing them.

| Step | Verb | When it runs | Price | Success rate |
| --- | --- | --- | --- | --- |
| 1 | [`ContactPoint.findEmail`](/code/data/people/find-email) | Always: routes walked cheapest first, up to `maxCost`; a miss is free and ends the run. | About 0.45¢ an address found | 95% of 3,296,127 calls |
| 2 | [`ContactPoint.verifyEmail`](/code/data/people/verify-email) | On a found address. A catch-all result runs again at `depth: deep` when `maxCost` leaves room. | About 0.18¢ an address | 99.99% of 2,098,997 calls |
| 3 | [`ContactPoint.findPhone`](/code/data/people/find-phone) | Only when `reach` holds `phone`. | About 1.7¢ a number found | 92.9% of 1,002,342 calls |

## Inputs
| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `prospect` | `Prospect` | Required | A saved Prospect id. Also served at `/prospects/{prospect}/verify`. |
| `reach` | `('work-email' \| 'phone')[]` | `['work-email']` | What to find and check. A phone is its own job with its own price. |
| `maxAge` | `string` | `'90d'` | A check younger than this is kept, at $0, instead of checking again. |
| `maxCost` | `number` | `0.02` | The most this Prospect may cost, in USD. The find walks its routes cheapest first and stops here. |

## Outputs
| Field | Type | What `null` means |
| --- | --- | --- |
| `prospect` | `Prospect` | Never null: the Prospect the result was written onto. |
| `emails` | `{ address: string; result: EmailResult; checkedAt: string }[]` | Never null: empty when no address was found, at $0. |
| `emails[].result` | [`EmailResult`](/code/data/types#emailresult) | Never null on a found address. |
| `phones` | `{ number: string; lineType: 'mobile' \| 'landline' \| 'voip' \| null; checkedAt: string \| null }[]`, in E.164 | Never null: empty unless `reach` holds `phone` and a number was found. |
| `reachable` | `boolean` | Never null: `true` when at least one address is `deliverable`. |
| `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 |
| --- | --- | --- | --- |
| `reachable` | `false` | No deliverable address: a miss, an invalid mailbox, or a catch-all domain. | Leave them out of the batch; you paid nothing for a miss or a catch-all. (the free-miss rule) |
| `emails[].result` | `catch-all` | The domain accepts every address, so the mailbox cannot be confirmed. | Send catch-alls in their own batch, at most 20% of a day’s sends. (our planning number for cold email) |
| `emails[].result` | `deliverable` across a batch | Safe to send, one by one. | Stop any batch whose hard bounces pass 2%, and re-check before you resume. (the 2% bounce line, our planning number) |
| `cost.usd` | About 30¢ for 50 | One live run found and checked 50 heads of operations at US property managers for 29¢. | Budget about 0.6¢ a reachable person, and leave the 2¢ cap on. (a measured run of 136 calls) |

## Effects
**Action class**

None: it reads, so no approval rule names it

**Modes**

- `try`: Finds and checks and returns the result; nothing is written onto the Prospect 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**

Writes the addresses, their results and the outreach state onto the Prospect. Readings about people are never shared across startups.

**Emits**

`Prospect.verified` with `{ prospect, emails, phones, reachable }`

**Performed by**

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

**Price**

About 0.6¢ a verified address. At most 2¢ a verified address. Free: a miss: no address found, a check that ends catch-all or unknown. `maxCost` defaults to 2¢. 50 verified leads (one live run spent 29¢): 50 × 0.6¢ a verified address is about 30¢.

**Reliability**

94.9%: the product of its steps’ rates, each on its own page. A step that fails falls back as that step does.

**Freshness**

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

**Batch and pages**

Up to 1 Prospect 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
Find and check Dana Whitfield’s work email before I write to her; spend
up to 2¢.
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/prospects/verify?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prospect":"prospect_AhvINObofR","maxCost":0.02}'
```

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

**CLI**

```bash
npx startup-builder do Prospect.verify --try --data '{"prospect":"prospect_AhvINObofR","maxCost":0.02}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Prospect.verify({ prospect: 'prospect_AhvINObofR', maxCost: 0.02 })"
  }
}
```

**SDK**

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

const result = await $.try.Prospect.verify({
  prospect: 'prospect_AhvINObofR',
  maxCost: 0.02,
})
```

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 |
| --- | --- | --- |
| `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
- **Jobs:** [Earn first links](/capabilities/earn-first-links) · [Find your first customers](/capabilities/find-customers)

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