The Envelope: one contract for every AI surface

ChatGPT, Perplexity, Gemini, Copilot, Google AI, and Claude return wildly different shapes. The Envelope normalizes all of them into one stable, versioned contract, with provenance on every field.

Integrating AI surfaces one at a time is a losing game. ChatGPT streams one shape, Perplexity cites another way, Google's AI Overview buries sources in rendered HTML, Gemini and Copilot each have their own DOM, and the model APIs return something different again. Write N parsers and you now maintain N parsers, each one breaking on its own schedule when a surface ships a redesign.

The AI Search API solves this with one design decision: every capture returns the same Envelope. Same shape for every surface, every acquisition method, every region. You write one parser, once.

What's in the Envelope

Fetch any completed child job and you get three top-level sections:

{
  "job":        { "id, surface, region, status, warnings, artifacts": "..." },
  "provenance": { "official, model, region, loginState, stopReason": "..." },
  "answer":     { "text, markdown, blocks": "..." }
}
  • answer, the response as text, as rendered markdown (always populated), and as structured blocks. This is what the AI actually told the user.
  • provenance, how the answer was obtained (more on this below).
  • job, identity, status, warnings[], and the artifacts key for the durable raw capture (the verbatim upstream payload, re-fetchable through the API).

A ChatGPT browser capture, a Google AI Overview browser render, and a Claude answer served by the official Anthropic API all come back in exactly this shape. The provenance block tells you how they differ; the structure never does.

Provenance is a first-class citizen

The reason a single contract works across such different surfaces is that we don't flatten the differences away. We record them, in provenance:

  • official, whether an official provider API served the result (true) or a real browser session saw it on the live page (false). You never confuse the two.
  • model, { providerId, observedLabel, inferred, confidence }. Was the model label read off the page or deduced? The inferred flag says so.
  • region, requested versus effective. If a surface served a US experience despite a Japanese egress, you see the drift (and a region_overridden warning) instead of trusting it blindly.
  • loginState, captchaEncountered, stopReason, proxyGeo, the operational truth of the capture.

This is what a real data contract looks like in practice: not just the answer, but a machine-readable account of exactly how it was obtained and which fields are ground truth versus inference.

Observed vs inferred, and absence as data

Two honesty guarantees ride inside the Envelope and matter regardless of surface:

Observed vs inferred. provenance.model.inferred tells you whether the model label was read off the page or deduced. You always know which fields are ground truth and which are our best guess.

Absence is data. If a surface returns no answer, the child still reaches a terminal completed status, provenance.surfacePresent is false, and warnings includes surface_absent. A blank answer is a measurement, not a failure. A lane that's genuinely unbacked (no credentials or engine) fails loud with DRIVER_UNAVAILABLE instead of a mock.

One contract, one integration, N surfaces

Because the shape is stable and versioned (GET /v1/health reports the current schemaVersion), you can:

  • Diff surfaces, compare how ChatGPT and Perplexity answer the same prompt, keyed on the same fields.
  • Fan out across regions, one request, one child per market, every child in the same shape.
  • Add a surface without touching your parser, when a new surface (or the authenticated Claude browser lane) ships, it arrives in the Envelope you already read.

That's the point of a contract. The surfaces will keep changing shape. They always do. The Envelope is the stable thing you build on, so that when a surface redesigns overnight, your integration doesn't.

→ See the Canonical Envelope reference for every field and the full warnings[] catalog.