Reference

POST /v1/enrich and the enrichment profile

The enrichment lane on its own: who is this person, what company are they at. One shape regardless of which vendor answered, with a coverage figure that says how much of it actually came back.

Most callers never call this directly: /v1/decide runs the same lane lazily, only when a rule needs a field from it. Call /v1/enrich when you want the profile without a policy.

Request

curl -X POST https://api.sortance.com/v1/enrich \
  -H "Authorization: Bearer $SORTANCE_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "mia@acme.com", "context": { "fields": ["company"], "max_cost_usd": 0.2 } }'
FieldTypeMeaning
emailstringRequired.
context.fieldsstring[]"person", "company" or both. Omitted means both. A group you do not ask for is never bought.
context.max_cost_usdnumberCeiling for this call. Default 0.10.
context.max_latency_msnumberDefault 3000.
context.strategyenumwaterfall (default): ask the next vendor only when the last had no match, an error, or covered under half the fields. fanout: ask every eligible vendor in parallel and fill disagreement on every record — at the price of paying each one.

What the lane does

  1. Cache. Person facts are cached under a keyed hash of the address and company facts under the domain, each for 30 days. A hit costs nothing.
  2. Company first, by domain. Company lookups send only the domain. A person lookup, which sends the address, runs only when you asked for person fields.
  3. Consumer mailboxes are skipped. A Gmail address gets neither lookup: gmail.com is not a company and a person lookup on a personal address is the most privacy-sensitive, least useful thing this lane could do. The profile comes back with coverage 0.
  4. Waterfall. People Data Labs is asked first; Apollo only when PDL had no match, failed, or covered under half the fields. Vendors bill per match, so asking both on every call would double the bill on every hit.
  5. Merge. When both answered, a field-level precedence picks the winner — Apollo for title, seniority and department; PDL for headcount; otherwise the fresher answer — and any difference is recorded in disagreement.
  6. Never exceed the ceiling. A lookup that would cross max_cost_usd or start too late to finish inside max_latency_ms is skipped and its receipt says skipped.

An upstream outage degrades the profile; it never fails the call. The affected group comes back null with coverage 0.

The profile

Every field is present on every response. Absent facts are null, never omitted and never guessed. The response is the profile with cost_usd, latency_ms and trace_id alongside, at the top level.

FieldTypeMeaning
personobject | nullnull when no person lookup ran or matched.
person.full_name, first_name, last_namestring | nullAs the vendor gives them. Personal data: returned to you, never stored on a decision.
person.titlestring | nullThe vendor's title text. Personal data, same rule.
person.seniorityenum | nullc_suite, vp, director, manager, senior, entry, unknown.
person.departmentenum | nullengineering, product, design, marketing, growth, sales, customer_success, finance, legal, hr, operations, executive, other, unknown.
person.linkedin_urlstring | nullPersonal data, same rule as the name.
person.location{ country, region } | nullCountry is kept on a decision; region is not.
companyobject | nullnull when no company lookup ran or matched.
company.name, domainstring | null
company.industryenum | nullOne of the normalized industry values. See the list in the policies reference.
company.industry_rawstring | nullThe vendor's own label, for display only.
company.employeesnumber | nullThe integer when given, otherwise the midpoint of employees_range.
company.employees_rangestring | nullThe vendor's original range, e.g. 201-500.
company.revenue_rangestring | nullAs the vendor reports it.
company.foundednumber | nullYear.
company.countrystring | nullISO 3166-1 alpha-2, uppercase.
company.typeenum | nullpublic, private, nonprofit, government, education, unknown.
company.techstring[]Lowercased technology slugs. Empty when none were reported.
company.linkedin_urlstring | null
company.funding{ total_usd, last_round } | null
coverage{ person, company }Fraction (0–1) of the fields you asked for that came back. A group you did not ask for, or that was skipped, reads 0.
freshness{ person, company }The newest date a vendor gave for that group, or null.
sourcesobjectOne receipt per vendor considered: { status, ms, cost, fields }. status is match, no_match, error, timeout or skipped. cost is what that vendor charged on this call.
disagreementobjectWhere two vendors answered the same field differently, keyed by path then vendor: { "company.employees": { "pdl": 240, "apollo": 180 } }.
cache{ hit, ttl_s }Whether this came from the 30-day cache.
visitorobject | nullFrom a push source such as RB2B: { identified, first_seen, pages, source }. null when none.
cost_usdnumberWhat this call cost, summed over sources. 0 on a cache hit.
latency_msnumber
trace_idstringen_-prefixed.
Read coverage before you read the profile

A profile with company.industry: null at coverage 0.2 says almost nothing. Policies can read enrichment.coverage.person and enrichment.coverage.company directly, and an ICP score carries its own coverage for the same reason.

Not available yet

  • Your own vendor keys. The adapters accept a key your account holds (and record its cost as 0), but there is no endpoint to store one yet. Clearbit is reachable only that way, so it is not reachable today.
  • Choosing vendors per account. Every vendor configured on the deployment is eligible, in the order above.
  • A monthly enrichment allowance. 402 enrichment_quota_exceeded is reserved for it and is not returned today; each call is bounded only by its own max_cost_usd. Calls are not counted against the verification allowance.
  • A batch endpoint. One address per call.

Errors

StatusCodeWhen
400invalid_requestMalformed body or a missing address.
401unauthorizedMissing, malformed or revoked key.
429rate_limitedMore than 600 requests a minute, counted together with verify, decide and the configuration endpoints.
500internal_errorA bug. Carries the trace_id.