Contact .enrich
Fill a Contact’s missing title, employer, seniority and location from outside sources, each value marked with where and when it was read.
- Price
- 20¢ per 100 contacts
- Success rate
- 99.99% of 136,987 calls
- Freshness
- Fresh for 90 days
- Emits
Contact.enriched- Capability
- Enrichment
POST https: //api.sb/ {your-domain}/ contacts/ enrich
Options and prices
Inputs
| Name | Type | Default | Meaning |
|---|---|---|---|
contact | Contact | Required | A saved Contact id. Also served at /. |
fields | ('title' | 'organization' | 'seniority' | 'location' | 'profiles' | 'history')[] | every empty field | What to fill. A field you already hold is skipped and costs nothing. |
maxAge | string | '90d' | A value read more recently than this is kept, at $0, instead of reading again. |
maxCost | number | 0.05 | The most this Contact may spend, in USD. Over it, the call is refused before it spends, or stops early with what it has. |
Outputs
| Field | Type | What null means |
|---|---|---|
contact | Contact | Never null: the Contact the values were written onto. |
filled | string[] | Never null: the fields this call filled; empty when no source knew the person, at $0. |
values | Record<string, | Never null: one entry per filled field, each with when and how it was read. |
values.organization.value | Hostname | No current employer on record. |
values.seniority.value | 'owner' | 'c-level' | 'vp' | 'director' | 'manager' | 'individual' | Unknown. |
cost | 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 |
|---|---|---|---|
values.organization | Not the company the Contact was found at | They changed jobs. The old address will bounce or reach whoever inherited the inbox. | Stop their Sequence and find them again at the new company; a new job is a reason to write. (the job-change rule for cold email) |
filled | Empty from an email address alone | An email resolves a person less often than a profile URL: about 72% across every kind of identity, against 99.99% from a profile URL. | Save the profile URL Prospect.find returns and enrich from that. (measured across 394,379 routed calls) |
values.*.observedAt | Older than 90 days | Titles drift: a quarter-old title is a guess about today. | Read it again before a first email that quotes the title. (our planning number) |
Effects
- Action class
None: no approval rule names it, so it runs within its seat’s Permissions and Limits
- Modes
try: Reads the values and returns them; nothing is written onto the Contact 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-Keywith the same input replays the first answer at $0; with other input it returns 409.- Saved or shared
Writes each filled value onto the Contact with where and when it was read; a value you entered is never overwritten. Readings about people are never shared across startups.
- Emits
Contact.enrichedwith{ contact,filled, values } - Performed by
- Price
About 20¢ per 100 contacts. Free: a Contact no source knows, fields you already hold.
maxCostdefaults to 5¢. Every Contact in a 300-person list: 300 × 0.2¢ a contact is about 60¢.- Reliability
The first route succeeds 99.99% of 136,987 calls. If it fails, a second route answers at 99.98% of 200,979 calls, and you pay for one.
- Freshness
A reading younger than 90 days returns at $0. Readings about people are never shared across startups.
- Batch and pages
Up to 1 Contact a call. It answers in one response.
- Rate
The shared rate rules: 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: filter-unsupported, input-missing, limit-exceeded, max-cost, rate-limited, sources-busy, venue-unsupported.
When it waits
Returns in one response; nothing waits.
Example
Fill in what we know about Dana Whitfield before I write to her; spend
up to 1¢.To retry, send the same Idempotency-Key: the retry returns the first answer for $0. A new key reads again.
Test values, and watching it
A sk_test_ key answers these the same way every time, at $0 (every test value).
| Test value | Always returns | What it is for |
|---|---|---|
maxCost: | Refuses with 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 | Any call naming this hostname answers 429 with Retry-After, so your retry logic runs. |
sources-busy.test | Refuses with sources-busy | Any call naming this hostname answers 503 as if every route were busy, and nothing is spent. |
Watch it on a schedule: a Monitor runs this call and records an Event only when the reading changes.
await $.Monitor.create({
verb: 'Contact.enrich',
input: { contact: 'contact_FdmRdAKcbV', fields: ['organization', 'title'] },
change: 'job-change',
every: '4w',
})Used by
- Workflows: Champion moved · Inbound routing · List refresh · Post-engager harvest
- Agent tasks: Account brief · Answer every inbound
- Jobs: Find your first customers · Keep and grow customers