Skip to main content

Concept · 8 min read

Anatomy of an enrichment.

One API call returns roughly forty-three person fields. Here's the exact response shape, where every value comes from, and the prompts behind the AI-synthesised ones — using a fictional persona, Maya Patel, that we use across our marketing examples.

The input

Either field works alone. Both together is the strongest hint set — email anchors the company match, LinkedIn URL skips the search step.

POST /v1/enrichments
x-api-key: YOUR_API_KEY
Content-Type: application/json

{
  "type": "person",
  "input": {
    "email": "maya.patel@northwindtools.com",
    "linkedInUrl": "https://www.linkedin.com/in/maya-patel-dg"
  }
}

The response

A flat field map under result, plus per-field source attribution and per-source raw responses (truncated here for readability — full schema in the API reference).

{
  "id": "enr_01h9z3k4m5n6p7q8r9s0t1",
  "status": "completed",
  "entityType": "person",
  "result": {
    /* identity ─────────────────────────────────────────── */
    "full_name":           "Maya Patel",
    "first_name":          "Maya",
    "last_name":           "Patel",
    "email":               "maya.patel@northwindtools.com",
    "email_status":        "verified",
    "email_confidence":    0.97,
    "linkedin_url":        "https://www.linkedin.com/in/maya-patel-dg",
    "phone_number":        null,

    /* role ─────────────────────────────────────────────── */
    "title":               "VP of Demand Generation",
    "normalized_title":    "VP, Marketing — Demand Gen",
    "seniority_level":     "VP",
    "management_level":    "Senior leader (manages managers)",
    "department":          "Marketing",
    "subdepartment":       "Demand Generation",
    "functions":           ["paid", "lifecycle", "events", "rev-ops handoff"],
    "tenure_years":        2.5,
    "reports_to":          "CMO",
    "team_size":           7,

    /* company ──────────────────────────────────────────── */
    "company_name":        "Northwind Tools",
    "company_domain":      "northwindtools.com",
    "firm_name":           "Northwind Tools, Inc.",
    "company_linkedin_url":"https://www.linkedin.com/company/northwind-tools",
    "office_location":     "San Francisco, CA",
    "city":                "San Francisco",
    "state_region":        "California",
    "country":             "United States",

    /* signals ──────────────────────────────────────────── */
    "previous_firm":       "HubSpot",
    "education":           "MBA, Kellogg (2018) · BSc Economics, UC Berkeley",
    "skills":              ["demand gen", "lifecycle", "RevOps", "MarTech selection"],
    "tech_stack":          ["HubSpot", "Apollo", "Clay", "Mutiny"],
    "recent_activity":     "Spoke at SaaStr 2025: 'Agent-led demand gen, end-to-end'",

    /* synthesis (LLM) ──────────────────────────────────── */
    "outreach_angle":      "Lead with Northwind's recent ARR milestone and Maya's HubSpot pedigree. Connect to her recent SaaStr keynote on agent-led demand gen. Reference the open req for a Senior Lifecycle Marketer — she's actively expanding the team.",
    "contextual_insights": "Demand-gen leader scaling Northwind from $4M to $28M ARR over thirty months. Came from HubSpot's PLG team. Operator profile — cares about pipeline-conversion math, allergic to vanity metrics.",
    "priority_score":      0.92,
    "confidence_score":    0.91,

    /* metadata ─────────────────────────────────────────── */
    "data_source":         "abm.dev v1",
    "last_enriched":       "2026-05-09T14:22:11Z",
    "last_verified_date":  "2026-05-08"
  },
  "fieldAttribution": { /* every field above mapped to source, see below */ },
  "sourceResults":    [ /* per-source raw response, see below */ ]
}

Where every field comes from

Five buckets — identity, role, company, signals, synthesis — with the source, the extraction method, and a confidence number on each. The synthesis bucket is the one most APIs hand-wave; we walk through the actual prompts below.

Identity

FieldSourceHow it's derivedConf.
full_nameLinkedInProfile header0.99
first_name / last_nameLinkedInHeader tokenisation0.99
emailHunter.ioEmail finder against `northwindtools.com` + name0.97
email_statusHunter.ioMX + SMTP RCPT probe (catch-all rejected)0.97
linkedin_urlInput · LinkedIn search fallbackTrusted as input; otherwise resolved by name + company match1.00 (input)
phone_numberNot found in any source. Returned as null, not made up.Hallucination guard.

Role

FieldSourceHow it's derivedConf.
titleLinkedInCurrent position headline0.98
normalized_titleSynthesisMaps free-form titles to a canonical taxonomy (function + level)0.92
seniority_levelSynthesisTitle classification0.96
management_levelSynthesisCombines title + team_size + reports_to chain0.86
department / subdepartmentSynthesisTitle + functions vector0.94
tenure_yearsLinkedInCurrent position start date → today0.99
reports_toSynthesisInferred from company size + dept structure (not from a graph lookup)Confidence stays low when we can't verify — this would be flagged for the writeback diff before HubSpot accepts it.0.71

Company

FieldSourceHow it's derivedConf.
company_nameLinkedInCurrent company on profile0.99
company_domainEmail · LinkedInEmail-domain extraction first; LinkedIn website fallback0.99
firm_namePerplexityLegal entity name pulled from public filings + Crunchbase0.93
company_linkedin_urlLinkedInLinked from the person's profile0.99
office_location · city · state · countryLinkedIn · PerplexityProfile location — cross-checked against the company's HQ string0.95

Signals

FieldSourceHow it's derivedConf.
previous_firmLinkedInMost recent prior position0.99
educationLinkedInEducation section0.96
skillsLinkedInTop skills — capped at 4 to keep the field useful, not a wall0.88
tech_stackTavily · PerplexityWeb search across her conference talks, BuiltWith, public job postsConfidence reflects that tech-stack inference is partial — only what they've talked about publicly.0.78
recent_activityTavilyWeb search for `"Maya Patel" SaaStr` in last 12 months0.94

Synthesis (LLM)

FieldSourceHow it's derivedConf.
outreach_angleSynthesis (Claude)Aggregates identity + role + signals + company news through a tuned prompt0.85
contextual_insightsSynthesis (Claude)Same upstream context, different prompt geared at persona description0.83
priority_scoreSynthesisWeighted score: ICP fit + recent intent + reachability0.92
confidence_scoreSynthesisAggregate of source confidences, weighted by field importance

The synthesis prompts

Two of the forty-three fields — outreach_angle and contextual_insights— are written by an LLM (Claude) on top of the structured signals from the other sources. Most enrichment APIs treat their prompts as a black box; ours don't. Here they are.

outreach_angle prompt

# Role
You write the opening pitch for B2B outbound.  Your job is one or two sentences
that the rep can paste into LinkedIn or email — specific, recent, and not
clichéd.

# Inputs (provided by the pipeline, not by the user)
- The person's full_name, current title, current company, tenure, and the
  three most recent activity signals (talks, posts, hires, funding).
- The company's last six months of public news (Perplexity).
- The user's standing system_prompt + ICP description (from
  /v1/enrichment-config).

# Constraints
- Reference at least one specific, recent, verifiable fact.
- No empty flattery ("I love what you're doing at X").
- No assumptions about pain ("I bet you're struggling with…").
- If you can't ground the angle in a concrete fact, return null and surface
  via outreach_angle_status: "insufficient_signal".  Don't make one up.

# Output
A single string, 1–2 sentences, second-person, no emoji.

contextual_insights prompt

# Role
You write the persona-level summary that gives the rep enough to know how to
talk to this person — operator vs strategist, what they care about, what
they're allergic to.

# Inputs
- The same context bundle as outreach_angle.
- Plus: their public writing (LinkedIn posts, conference talks, podcast
  appearances) summarised by Perplexity.

# Constraints
- 2–3 sentences.
- Specific verbs, no jargon.  "Cares about pipeline-conversion math" beats
  "data-driven leader."
- Acknowledge gaps openly — if the public footprint is thin, say so rather
  than pad.

# Output
A single string, 2–3 sentences.  Third-person.

Override per request

Both prompts above are the platform defaults. Override them per-organisation via PUT /v1/enrichment-config for a permanent change, or per-call via options.instructions on the enrichment request for a one-off override (e.g. tighter ICP, different voice, different emphasis).

What we don't do

If the cross-source check fails on a field, we return null and surface a quality_warnings entry. We never fill a gap with a plausible-sounding made-up value. The audit trail at GET /v1/enrichments/{id}/sources shows every provider that was queried, what they returned, and which values were reconciled against which.

Related