Skip to main content
5 min read

Field envelopes

Every leaf value in a source envelope — name, head count, description, anything — is wrapped in the same FieldEnvelope shape. There is no field on ABM.dev that returns a bare string with no provenance. This page is the contract.

The shape

{
  "value": "Monzo Bank Limited",
  "source": "companies-house",
  "sources_observed": ["companies-house", "linkedin", "perplexity"],
  "confidence": 0.97,
  "rationale": "Three sources agree on the registered legal name. Companies House is canonical for UK registrations.",
  "citations": [
    "https://find-and-update.company-information.service.gov.uk/company/09446231"
  ],
  "as_of_date": "2024-11-01",
  "observed_at": "2026-05-14T09:21:00Z",
  "volatility_class": "slow",
  "privacy_class": "public_record",
  "conflicts": [
    {
      "value": "Monzo Bank",
      "source": "perplexity",
      "confidence": 0.82,
      "citations": ["https://en.wikipedia.org/wiki/Monzo"],
      "as_of_date": "2024-08-15"
    }
  ]
}

The TypeScript and Python SDKs (when Plan 6 ships them) will expose this as a generic FieldEnvelope<T> where T is the underlying value type — string, number, enum, nested object.

Field by field

FieldTypeWhat it carries
valueTThe winning value. Typed (string, integer, enum, nested object). Null only when no source produced a defensible answer.
sourcestringThe source that won — e.g. companies-house, linkedin, perplexity, tavily, synthesis. When two sources agreed equally, the more authoritative one wins.
sources_observedstring[]Every source that produced a value for this field, agreement or not. Useful for auditing breadth of coverage.
confidencenumberBetween 0 and 1. How sure we are, given everything we looked at. See Confidence, citations, conflicts for bands.
rationalestringOne-line explanation of why this value won. Read by the audit log and the approval surface.
citationsstring[]URLs we actually pulled the value from. No citations, no claim. Click through and audit.
as_of_datedateISO date — when the fact was true in the world. A registered name from 2015 has as_of_date: 2015-01-23 even if we checked it yesterday.
observed_attimestampISO timestamp — when we last verified the value. Drives staleness checks.
volatility_classenumslow (legal name, founding year), steady (head count, tech stack), volatile (open roles, recent news). Tells your scheduler what’s worth re-fetching.
privacy_classenumpublic_record, derived_inference, personal_data, special_category, regulated. Helps your downstream workflows route values into the right tooling.
conflictsConflictValue[]When two sources disagreed, we kept the loser here with its own citation. You decide whether to override the winner.

Four pieces of provenance, four reasons

Two dates, not one

as_of_date tells you when the fact was true; observed_attells you when we last looked. A registered name hasn’t changed since 2015 — we still re-checked yesterday. Both matter to a downstream decision-maker.

Volatility, not freshness alone

A monthly re-check on legal name is wasted money. A monthly re-check on head count is barely enough. The volatility class is how your scheduler tells the two apart.

Conflicts preserved

Other enrichment APIs collapse disagreement silently. We don’t. The runner-up is in conflicts[] with its own citation. If your domain knowledge says the loser is right, you have everything you need to override.

Privacy class as a routing key

personal_data and special_category tell your pipelines “send this through the consented- marketing channel, not the lead-router.” The shape doesn’t make the choice for you — it makes the choice possible.

Where you’ll see them

Source v2 only — for now

Every leaf in /v1/enrichments/{id}/source is wrapped in this envelope. Legacy /v1/enrichments/{id} still returns the flatresult_data shape during the migration period; new code should target /source.

Research-angle results carry the same envelope shape — same confidence, same citations, same dates. Destination values carry a slightly different shape (they record the composition inputs instead of the winning source) but the same provenance contract.

Keep reading