Read why contacts were or were not enrolled
The enrolment decision trace for one workflow, newest first — the answer to "why isn't this contact in this journey".
Every enrolment decision is recorded, positive and negative: the run
counts elsewhere tell you who enrolled and say nothing about the far
larger number who did not. reason_counts is the shape of it, and the
rows are the evidence.
Retained for 14 days. This is a debugging aid, not a record.
Recipient email is not exposed — contact_id is the join key.
Path parameters
id string required The workflow's UUID or, when the value does not parse as a UUID, its org-unique key. Unknown handles read as 404 not_found. Accepted on every workflow route, read and write alike.
Query parameters
contact_id string optional Narrow to one contact, by its id, your own external_id, or its email address. The two non-id forms additionally require api.contacts.view, so a workflows-only key cannot use this filter to learn which addresses exist; without it they are 403 invalid_scope. A handle that names no contact is 422 validation_failed, not an empty page.
since string<date-time> optional RFC 3339. Clamped to the 14-day retention — an earlier value reads as the start of the window rather than failing.
limit integer optional Responses
Errors follow the RFC 7807 problem format — see the error reference.
data array<object> required Each entry in data:
workflow_id string<uuid> required contact_id string<uuid> required cause string optional The trigger kind: segment, event, activity or date_rel.
source_key string optional The segment key, event name or activity name the signal fired on.
decision string enum required errored is the enrolment path failing rather than declining — a distinction worth keeping, because a contact missing for either reason looks identical from outside.
One of: enrolled, skipped, errored
reason string optional Which exit was taken. One of workflow_not_active,
trigger_stale, before_forward_floor,
reentry_once_already_ran, success_exit_still_matches,
entry_predicate_false, already_live_run,
outside_shadow_cohort, source_unparseable or
enroll_insert_failed. Absent on an enrolled decision, which has
no reason to give.
detail string optional Whatever makes the reason actionable — the predicate text, the forward floor and the event instant, the workflow's status.
run_id string<uuid> optional The run an enrolled decision produced. Absent on a skip.
shadow boolean required Whether the decision was taken while the workflow was shadowing.
occurred_at string<date-time> required reason_counts object optional The tally by reason over the same window. Present only when the read is narrowed to one workflow — a count mixed across every workflow a contact ever touched answers no question anybody asked.
validation_failed Problem. When the refusal is about a
reference — a segment key, template slug, topic or attribute name the
organization does not have — it additionally carries validation_code,
field, line/column, missing, candidates and next_step, so a
client can correct the call without a second round of guessing. See
ValidationProblem.
application/problem+json code is internal_error. The
request_id field can be quoted to SendOps support to investigate.
application/problem+json