Concepts
Adverse action
How an AdverseAction node fires, the notice stages and flows, the draft-first default, the webhook events it emits, and where the notice PDFs end up.
When a cascade declines an applicant on the basis of a consumer report, the FCRA requires an adverse-action notice. In Cascades that is an Action node with action_type: "AdverseAction": it picks the reasons from the conditions that failed, renders the operator's notice template to a PDF, emails it to the applicant, and keeps the archive. The API sees the results as execution state, webhook events and notice artifacts.
For the legal background and the operator's obligations, see Adverse action notices.
Where does the node sit, and why does it matter?
An AdverseAction node must be downstream of an IfElse decision, and its branch decides its mode:
| Placement | Mode | Runs when |
|---|---|---|
In the decision's human_review_steps |
Human review | The decision flags the execution for review — the draft is rendered before the pause; a reviewer sends or skips it on denial |
In the decision's fail_steps |
Rejected | The decision auto-fails the applicant |
In pass_steps, or not under any decision |
invalid | The cascade will not save |
Draft first, or auto-send?
A decline never delivers a notice unattended unless the node opts in. The node's send_strategy is:
draft_first(default) — the executor parks an editable draft (reasons, recipient, rendered PDF) and the execution waits. An operator reviews and sends it from the app; sending records the denial and delivers in one step. Approving the applicant instead discards the draft.auto_send— an explicit opt-in for unattended API and batch runs: the notice is generated and emailed immediately.
Human-review mode pre-generates the draft when the execution is flagged, so the reviewer's Deny dialog already shows the pre-selected reasons and lets them edit or skip the notice (a skip is recorded as notice_intentionally_skipped in the audit trail, never as compliant).
Which reasons go on the notice?
The executor looks at the conditions that matched in the upstream decision and maps each condition's field to an FCRA reason key through a compliance-editable mapping (fico or credit_score → credit_score_low, inquir → too_many_inquiries, eviction → eviction_record, and so on). Thresholds are never encoded in the mapping — whatever threshold the operator wrote is the threshold. Reviewers can override the list before sending. The full reason vocabulary is listed on the compliance page.
Notice stages and flows
flow follows the cascade's use case and drives the stage sequence:
| Flow | Stages | Notes |
|---|---|---|
employment |
pre_adverse → waiting period → post_adverse |
Pre-adverse notice with a copy of the report and the Summary of Rights; the execution enters waiting; the waiting period is floored to 5 days; then the final notice |
tenant_screening |
final |
Single notice |
credit_lending |
final |
Single notice |
Which webhooks fire?
| Event | When |
|---|---|
adverse_action.triggered |
Notice handling began. Employment: pre-adverse sent and the wait scheduled. Single-notice flows: final notice sent |
adverse_action.completed |
Obligations fulfilled. Employment: post-adverse sent on resume. Single-notice flows: same instant as triggered |
adverse_action.failed |
A notice email could not be delivered |
adverse_action.retry_scheduled |
Employment only: the post-adverse send failed and a retry is scheduled (retry_at) |
Payloads carry workflow_execution_id, node_id, external_reference, flow, notice_stage and, where relevant, sent_to and completed_at. Details per event are under Webhook events.
Where are the PDFs?
- Per execution:
GET /executions/{id}/notice_artifactslists the notice PDFs;GET …/notice_artifacts/{artifact_id}downloads one (executions:read:full). - Per batch:
GET /batches/{id}/adverse_action_notices.zipbundles every applicant's notices (404while none exist).
The archive is immutable: a resend from the audit dashboard re-delivers the exact bytes that were sent, with a [Resent] subject prefix.
Templates
Notice templates are per operator, versioned (each upload is an immutable version), and can be assigned as the default for a bureau (equifax, experian, transunion, lexisnexis, cic) so the notice matches the report that drove the decision. Email subject and body are customisable with {{applicant_name}}, {{notice_date}} and {{recipient}} placeholders. Manage them at /adverse_action_templates; there is no API for template management yet.