Read a contact's enrolment decisions across every workflow

Search Documentation

Search across all developer documentation

workflows

Read a contact's enrolment decisions across every workflow

GET /v1/contacts/{email}/enrollment-decisions
Auth required api.workflows.view

Every enrolment decision taken about one contact, across every drip workflow, newest first — the contact-side view of the trace behind GET /v1/workflows/{id}/enrollment-decisions.

Retained for 14 days. This is a debugging aid, not a record.

No reason_counts: the tally is only filled when the read is narrowed to one workflow, because a count mixed across unrelated journeys answers no question anybody asked.

The path segment is a contact id OR an email address. The id form needs only api.workflows.view, the scope that governs the rows returned. The email form additionally requires api.contacts.view, because resolving an address or an external id to a contact is a contacts read and a workflows-only token must not be able to use this route to learn which addresses exist; without it the response is 403. Percent-encode @ as %40. The rows themselves never carry an address.

Path parameters

email string required

The contact's UUID, its email address (percent-encoded), or your own external_id. The two non-id forms are a contacts read and additionally require api.contacts.view.

Query parameters

since string<date-time> optional

RFC 3339. Clamped to the 14-day retention.

limit integer optional

Responses

Errors follow the RFC 7807 problem format — see the error reference.

200 A page of the contact's decisions application/json
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.

401 Missing, malformed, or unknown API key application/problem+json
403 Key lacks the required scope or plan limit violated application/problem+json
404 Resource not found application/problem+json
422 A query parameter, path value or body field failed validation. The body is a 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
429 Per-org rate limit exceeded application/problem+json
500 Unexpected server-side failure. The code is internal_error. The request_id field can be quoted to SendOps support to investigate. application/problem+json