resolved (was the issue fixed) and issue_category (which kind of
issue it was); a sales initiative can declare objection and next_step; an
onboarding initiative can declare blocked_on. Every closed conversation then
writes back the same fields, so you get one row per participant instead of a
transcript you’d have to read one at a time.
Extraction is scoped to one initiative. Each initiative has its own schema,
and there’s no shared, cross-initiative field catalog today.
Defining a schema
A schema is a map of field slugs to field definitions:
Every field also carries:
The field slug (the map key) must be lowercase, start with a letter, and
contain only letters, digits, and underscores, up to 40 characters.
topic,
problem, and competitor are reserved: Boom already writes its own
org-wide signals under those keys, so a custom schema can’t declare them:
extraction_schema_set rejects the request with a reserved_field error.
Applying a schema
- MCP
- REST
extraction_schema_set: author or replace the schema in one call.changed: false means the schema you submitted was identical to what was
already current: no new version was created, and the notes you sent were
not recorded (the response returns the prior version’s notes instead).
Read the current schema back with extraction_schema_get /
GET /api/v1/initiatives/{id}/extraction-schema. It returns the schema in
the same shape extraction_schema_set accepts, so you can fetch it, edit a
field, and set it back. hasSchema: false and schema: null mean the
initiative has none configured yet.
Setting a schema reshapes how every conversation on the initiative gets
analyzed, so it is not a casual change. Over MCP it requires the signed-in
user to be an org admin. Over REST it takes a valid organization API key, and
any valid key can make this call: treat a key as full access to your
organization and scope who holds it accordingly.
Reading the data back
Per participant
GET /api/v1/initiatives/{id}/participants/{participantId} returns a
values array: one entry per captured field, each with label,
valueKind, a display value, and (when available) the sourceQuote
and confidence behind it.Listed across participants
GET /api/v1/initiatives/{id}/participants (paginated) returns each
participant’s values as an object keyed by field slug, each value an
array of display strings, plus capturedAt.In aggregate
GET /api/v1/initiatives/{id}/data/summary (initiatives_summary)
returns participantCount plus one entry per variable: coverage
(answered/eligible), and depending on type, an average (numeric
fields), a distribution (categorical fields), or samples (text
fields).Using extracted values later
Once a schema is set (that’s what “publishes” it), its fields become addressable in journey message templates asengagement.extracted.<slug>,
for example engagement.extracted.issue_category. This only resolves once
the upstream conversation step that produces it has actually run; before
that, the placeholder goes out blank. See
Template variables for the full binding syntax and
the trigger-by-trigger resolution matrix.
Rules and limits
- A schema needs at least one field. An empty
fieldsmap is rejected. - Naming is the only structural limit. No cap on how many fields a schema declares, but the slug rules above are enforced.
- Changing a schema doesn’t retroactively update past extractions. Engagements already extracted keep the values produced by whichever schema version was current when they ran. The new version only applies to conversations extracted after the change. Re-running extraction against old, already-closed conversations under a new schema is a dashboard/CLI operation today, not something the public API or MCP exposes.
- Extraction only runs on conversations with enough signal: at least 3 total messages and at least 1 inbound message. Below that bar, a conversation is skipped rather than filled in with empty values. Plan for this when the audience is people who already went quiet: whoever never replies produces no fields at all, so coverage on a win-back reflects who answered, not who you reached.
- Repeat triggers don’t repeat the LLM call. Boom hashes the transcript together with the schema; if neither changed since the last extraction, it skips the call. Editing the schema changes the hash, so the next time that conversation closes (or a manual re-extract fires), it re-runs against the new fields.
- Scalar fields (
bool,int,enum,string) always get a value. Every field in the schema is required in the model’s output. There’s no built-in “unknown” or null state for these types; the model does its best with what the conversation gave it, confirming or updating any previously extracted value. List fields (enum[],string[]) are the exception: the model is instructed to emit an empty array when the conversation gives no signal.
Related
Template variables
Bind
engagement.extracted.* into a WhatsApp template placeholder.One uniform surface
How the same capability is exposed identically on REST and MCP.
Use MCP
Connect an AI tool to read and set extraction schemas conversationally.
API reference
Every Initiatives, Journeys, and Segments endpoint.