FCRA adverse action API: from pre-adverse notice to decision
For engineers at approved FCRA partners: what each adverse-action endpoint does, from the complete order through the pre-adverse notice, the waiting period, disputes, the post-adverse notice and the recorded decision. The process itself is for your counsel.
Updated November 3, 2026
What the FCRA adverse action API does, and what it leaves to counsel
When an end user is considering a decision against a consumer because of a report, the FCRA Partner API can run the two-step adverse-action process as the consumer reporting agency for direct and platform partners. You name the records the decision rests on; the API sends the consumer the pre-adverse notice with the report access code, waits the period the use location requires, and sends the post-adverse notice. The end user still makes the decision; the API never decides for it.
This page describes what each endpoint does: request, response, state machine, error codes. When adverse action is required, what a notice must contain and how long to wait are questions for your counsel; the adverse action process page and the FCRA overview are written for that conversation. A cra partner runs notices in its own process and uses only the order, dispute and adjudication endpoints here.
An FCRA order is a consumer report, which is why it requires an approved partner, a registered end user with permissible purposes, a consent attestation and a use location. Python and Node.js SDKs ship with the first registry release; see SDKs. Until then the raw HTTP calls below are the integration, and the sandbox runs the same flows on an os_test_ key, 60 calls a minute and 2,000 a day, with nothing billed.
Before adverse action: the FCRA order lifecycle
Adverse action hangs off a complete order, so the lifecycle comes first. Register the end user once with POST /v1/fcra/end-users and its permissiblePurposes. Then place the order with POST /v1/fcra/orders and an Idempotency-Key header: endUserId, permissiblePurpose, consent { obtainedAt, method, documentRef }, subject { firstName, lastName, dob, aliases[], addresses[] }, useLocation { state }, liveScope, searches[], clientReference and callbackUrl. The answer is 202 at status: "received"; poll GET /v1/fcra/orders/{order_id} or wait for order.completed until status: "complete". The creating orders guide walks every field.
The Idempotency-Key (200 characters or fewer) matters because the order is billed: a replay with the same body within 24 hours returns the original order with Idempotent-Replayed: true; a different body is 409 idempotency_conflict.
Every order carries consumerPortal { url, accessCode }. With the code, their last name and date of birth, the consumer can see exactly what was furnished and dispute any item without contacting you first; the pre-adverse notice carries the code so they can do that before the post-adverse notice goes. The consumer portal reference describes what they see.
The notices also need an address. An order from the hosted consent flow carries the consumer's e-mail; for an order placed through the API, set it with PATCH /v1/fcra/orders/{order_id}/consumer (email, phone, copyRequested, locale) before you start, or the start is 422 consumer_contact_required.
Read the adverse items, then start the pre-adverse action notice
GET /v1/fcra/orders/{order_id}/adverse-items returns every record the current report furnishes as an items[] entry with id, type (sex_offender, criminal, healthcare), searchId and a plain-language text, together with the rule the use location adds, or null when it adds none. The rule object carries state, city, purpose, version, waitingBusinessDays, individualizedAssessment, citation, note and alsoApplies[]. individualizedAssessment: true on the rule means the start will be refused without one.
curl https://sandbox.offendersearch.app/v1/fcra/orders/fo_ee0b34c5b52fd35b/adverse-items \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY"
# 200
{
"orderId": "fo_ee0b34c5b52fd35b",
"items": [
{
"id": "crim:IL-SANGAMON:2021CR004417",
"type": "criminal",
"searchId": "fs_9c03d7e1a4b6f258",
"text": "Sangamon County Circuit Court, case 2021CR004417: one misdemeanor count, disposition guilty, 2021."
}
],
"rule": {
"state": "IL",
"city": null,
"purpose": "employment",
"version": "…",
"waitingBusinessDays": 5,
"individualizedAssessment": true,
"citation": "…",
"note": "…",
"alsoApplies": []
}
}POST /v1/fcra/orders/{order_id}/adverse-actions starts the action. adverseItemIds (1–100 ids from the items list) is the only required key. reasonText (up to 4,000 characters) is your stated reason; waitingDays is 7 to 60 calendar days, default 7; individualizedAssessment is a boolean and assessmentText (up to 8,000 characters) is the written assessment where the rule requires one; medium is an optional map of delivery mediums.
curl -X POST https://sandbox.offendersearch.app/v1/fcra/orders/fo_ee0b34c5b52fd35b/adverse-actions \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"adverseItemIds": ["crim:IL-SANGAMON:2021CR004417"],
"reasonText": "The role requires unsupervised access to client funds.",
"waitingDays": 7,
"individualizedAssessment": true,
"assessmentText": "Nature of the offense, time elapsed and the duties of the role were considered: …"
}'The 201 is the adverse action at status: "pending". preNoticeSentAt records the moment the pre-adverse notice was delivered; postNoticeScheduledAt is when the post-adverse notice will go; postNoticeSentAt is null until it does. The order's own adjudication becomes pre_adverse_action at the same time.
{
"id": "fa_1b2c3d4e5f607182",
"orderId": "fo_ee0b34c5b52fd35b",
"status": "pending",
"adverseItemIds": ["crim:IL-SANGAMON:2021CR004417"],
"reasonText": "The role requires unsupervised access to client funds.",
"waitingDays": 7,
"rule": {
"state": "IL", "city": null, "purpose": "employment", "version": "…",
"waitingBusinessDays": 5, "individualizedAssessment": true,
"citation": "…", "note": "…", "alsoApplies": []
},
"individualizedAssessment": true,
"assessmentText": "Nature of the offense, time elapsed and the duties of the role were considered: …",
"medium": {},
"preNoticeSentAt": "2026-11-03T15:02:11Z",
"postNoticeScheduledAt": "2026-11-10T15:02:11Z",
"postNoticeSentAt": null,
"pausedAt": null,
"resumedAt": null,
"cancelledAt": null,
"cancelReason": null,
"disputeId": null,
"createdAt": "2026-11-03T15:02:10Z",
"updatedAt": "2026-11-03T15:02:11Z"
}A second start while an action is open is refused with 409 adverse_action_open, so a retried request cannot send a second notice. The adverse action reference describes every field.
The adverse action waiting period rule
The waiting period runs from the moment the pre-adverse notice is delivered, and its length is the latest of three things: your waitingDays (calendar days, default 7), the use location's rule in business days, and a floor of five business days that applies to every adverse action. A waitingDays below 7 is refused with 422 waiting_period_too_short before anything is sent. You can lengthen the period, never shorten it.
GET /v1/fcra/adverse-rules lists every rule the engine applies, by state, city and purpose, with its waitingBusinessDays, whether a written individualizedAssessment is required, and a citation. Read it at build time to know which use locations need an assessment collected up front, and read the rule on each adverse-items answer at run time, because the rules are versioned.
curl https://sandbox.offendersearch.app/v1/fcra/adverse-rules \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY"
# 200
{
"version": "…",
"defaultWaitingDays": 7,
"rules": [
{ "state": "IL", "city": null, "purpose": "employment", "waitingBusinessDays": 5,
"individualizedAssessment": true, "citation": "…", "note": "…" },
…
]
}When the period has run, the post-adverse notice goes, postNoticeSentAt is set, status becomes complete and the order's adjudication becomes post_adverse_action; cancel before then and adjudication clears.
Adverse action API error codes
Every refusal the FCRA API raises itself is { "detail": { "code", "message" } }; a body that fails field validation returns the standard validation list instead, so guard the shape of detail as the code tabs above do. The FCRA errors reference lists every code by endpoint.
| Status | Code | Meaning | What to do |
|---|---|---|---|
409 | order_not_complete | The order is not at status: "complete". | Wait for order.completed, then start. |
409 | adverse_action_open | A pending or paused action already exists on this order. | Read it with GET …/adverse-actions; cancel it first if the decision changed. |
422 | consumer_contact_required | The order has no consumer e-mail to send the notice to. | PATCH /v1/fcra/orders/{order_id}/consumer with email, then start again. |
422 | unknown_adverse_item | An adverseItemIds entry is not in the current adverse-items list. | Re-read GET …/adverse-items; the report may have been revised. |
422 | individualized_assessment_required | The use location's rule requires a written assessment and the request sent none. | Send individualizedAssessment: true and assessmentText. |
422 | waiting_period_too_short | waitingDays is below 7. | Send 7 or more, or omit it for the default. |
502 | pre_notice_not_delivered | The mail provider did not accept the pre-adverse notice; no waiting period started and the action was cancelled with cancelReason: "pre_notice_not_delivered". | Fix the address, then start again. adverse_action.notice_not_delivered also fires. |
409 | not_pausable, not_paused, dispute_open, not_cancellable | A pause, resume or cancel that the current state does not allow. | Read the action's status and disputeId first. |
Never retry a 4xx as if it were transient; each code above names a fix. The 502 is not transient either: the engine has already cancelled the action, and the fix is the consumer contact.
Disputes pause the adverse action clock: pause, resume, cancel
No waiting period runs while a dispute on the order is open. When a consumer disputes a furnished record, file it with POST /v1/fcra/orders/{order_id}/disputes: recordIds (1–100 furnished record ids), reason (not_me, inaccurate, incomplete, outdated, other), an optional consumerStatement and receivedAt. The 201 is the dispute at status: "received"; a dispute from the consumer portal is filed for you and reaches you as dispute.created. The FCRA dispute API page and the disputes reference cover the reinvestigation.
A dispute opened while the action is pending pauses it with the time that was left, sets pausedAt and disputeId, and emits adverse_action.paused. An action started while a dispute is already open starts paused. Resuming is refused with 409 dispute_open until every dispute on the order is resolved. When the last open dispute resolves verified, the action resumes on its own and adverse_action.resumed fires.
A dispute resolved modified, deleted or unable_to_verify changes what the report furnishes. The report is revised and the adverse action is cancelled with cancelReason: "report_revised", because the notice no longer matches the report. If the decision still stands, read the adverse items again and start a new action.
# A dispute arrives from the consumer (or through the consumer portal, which files it for you)
curl -X POST https://sandbox.offendersearch.app/v1/fcra/orders/fo_ee0b34c5b52fd35b/disputes \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"recordIds": ["crim:IL-SANGAMON:2021CR004417"], "reason": "not_me",
"consumerStatement": "This record belongs to a different person with the same name.",
"receivedAt": "2026-11-04T09:12:00Z"}'
# 201 → the dispute, status "received"; the pending adverse action becomes "paused" with the time that was left
# Pause and resume by hand (resume is 409 dispute_open while any dispute on the order is open)
curl -X POST https://sandbox.offendersearch.app/v1/fcra/adverse-actions/fa_1b2c3d4e5f607182/pause \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY"
curl -X POST https://sandbox.offendersearch.app/v1/fcra/adverse-actions/fa_1b2c3d4e5f607182/resume \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY"
# Cancel: the decision changed, or the report was revised
curl -X POST https://sandbox.offendersearch.app/v1/fcra/adverse-actions/fa_1b2c3d4e5f607182/cancel \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "client_cancelled"}'POST /v1/fcra/adverse-actions/{aa_id}/pause and /resume move the clock by hand, for a reason of your own; /cancel takes an optional { "reason" } (default client_cancelled, up to 200 characters) and sets cancelledAt and cancelReason. Each answers the updated action, or the matching 409 when the state does not allow it.
Notices, webhook events and adjudication after adverse action
Every consumer e-mail is recorded. GET /v1/fcra/adverse-actions/{aa_id}/notices lists the notices for one action and GET /v1/fcra/orders/{order_id}/notices every notice for the order: each has an id, a kind (pre_adverse, post_adverse, report_copy, dispute_received, dispute_resolved, …), to, subject, status (queued, sent, failed, skipped), sentAt, error, templateVersion and createdAt.
curl https://sandbox.offendersearch.app/v1/fcra/adverse-actions/fa_1b2c3d4e5f607182/notices \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY"
# 200
{
"notices": [
{ "id": "fn_…", "kind": "pre_adverse", "to": "consumer@example.com", "subject": "…",
"status": "sent", "sentAt": "2026-11-03T15:02:11Z", "error": null,
"templateVersion": "…", "createdAt": "2026-11-03T15:02:10Z" }
]
}
# Record the decision once the action is complete (or on an order with no adverse action)
curl -X POST https://sandbox.offendersearch.app/v1/fcra/orders/fo_ee0b34c5b52fd35b/adjudication \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"decision": "cleared"}'
# 200 → the order, with "adjudication" set; 409 order_not_complete | adverse_action_openThe adverse-action events arrive on the endpoint you registered with PUT /v1/fcra/webhook-endpoint, with data carrying adverseActionId, orderId, status and, where it applies, reason: adverse_action.created, adverse_action.completed, adverse_action.paused, adverse_action.resumed, adverse_action.cancelled and adverse_action.notice_not_delivered. GET /v1/fcra/events lists every event with its delivery state. The webhook example has the receiver; the FCRA webhooks reference has the catalogue.
// One receiver for every FCRA event — see /docs/learn/webhook-example for the verifier
switch (event.type) {
case "order.completed": enqueueReadAdverseItems(event.data.orderId); break;
case "adverse_action.created": markPreNoticeSent(event.data.adverseActionId); break;
case "adverse_action.paused": holdDecision(event.data.adverseActionId, event.data.reason); break;
case "adverse_action.resumed": releaseHold(event.data.adverseActionId); break;
case "adverse_action.completed": markPostNoticeSent(event.data.adverseActionId); break;
case "adverse_action.cancelled": reopenDecision(event.data.orderId, event.data.reason); break;
case "adverse_action.notice_not_delivered": pageCompliance(event.data.adverseActionId, event.data.reason); break;
case "dispute.created":
case "dispute.updated": enqueueFetchDispute(event.data.disputeId); break;
}Finally, record the decision. POST /v1/fcra/orders/{order_id}/adjudication with { "decision": "engaged" } or { "decision": "cleared" } answers the order with adjudication set, and is refused with 409 order_not_complete or 409 adverse_action_open while an action is still pending or paused. Adjudication records the end user's decision with the order; it sends nothing to the consumer.
Step by step: rehearse the adverse action flow on the sandbox
On the sandbox every test key is already a direct partner for every permissible purpose, and the subject's last name picks the outcome: Clear, Records (one furnished record per search), Withheld, Unavailable, Dispute (one furnished record whose dispute resolves deleted sixty seconds after you file it), Excluded, Sanctioned and Pending; any other name is Clear. Use Pat Records, 1980-01-01, for an order with something to take adverse action on, and Pat Dispute to watch a dispute cancel the action. The sandbox sends no e-mail; the notices list shows what would have gone. The FCRA sandbox reference lists every subject.
- Register an end user and place an order.
POST /v1/fcra/end-userswithpermissiblePurposes: ["employment"], thenPOST /v1/fcra/orderswith anIdempotency-Key, the subject Pat Records, 1980-01-01,useLocation.state, andsearches: [{ "type": "criminal" }]. PollGET /v1/fcra/orders/{order_id}untilstatus: "complete". - Set the consumer contact.
PATCH /v1/fcra/orders/{order_id}/consumerwith the consumer'semail, so the notices have somewhere to go. Skipping this is422 consumer_contact_requiredat the next step. - Read the adverse items and the rule.
GET /v1/fcra/orders/{order_id}/adverse-items. Keep theidof each record the decision rests on and readrule.individualizedAssessmentandrule.waitingBusinessDays. - Start the adverse action.
POST /v1/fcra/orders/{order_id}/adverse-actionswithadverseItemIds,reasonText,waitingDaysof at least 7, andindividualizedAssessmentplusassessmentTextwhere the rule requires it. Store the201and itspostNoticeScheduledAt. - Handle the events. Route the
adverse_action.*events from your registered webhook endpoint and fetch the action by id for the detail. - File a dispute and watch the clock.
POST /v1/fcra/orders/{order_id}/disputeson the furnished record; confirm the action ispaused. On the subject Dispute, the resolution sixty seconds later revises the report and cancels the action withcancelReason: "report_revised". - Record the decision. When the action is
completeorcancelled,POST /v1/fcra/orders/{order_id}/adjudicationwithdecision: "engaged"or"cleared", and readGET …/noticesfor the record of what was sent.
FCRA adverse action API questions engineers ask
Does the API decide whether to take adverse action?
No. It sends the two notices, keeps the clock and records the decision you report; whether to act is the end user's decision, made with counsel.
How is the waiting period calculated?
The latest of your waitingDays (7–60 calendar days), the use location's rule in business days from GET /v1/fcra/adverse-rules, and a five-business-day floor, counted from preNoticeSentAt. Below 7 is 422 waiting_period_too_short.
What is in the pre-adverse action notice?
The report access code for the consumer portal and the summary of rights, sent by the API as the consumer reporting agency, in the partner's branding for platform partners; the record is in GET …/notices with its templateVersion.
What happens when the consumer disputes during the waiting period?
The action pauses with the time that was left and adverse_action.paused fires. It resumes on its own when the dispute resolves verified, and is cancelled with cancelReason: "report_revised" when the resolution changes the report.
Can I run two adverse actions on one order?
Not at once: a second start while one is pending or paused is 409 adverse_action_open. After a cancellation, for example on a revised report, you can start a new one.
What this page does not cover
- Whether, when and how to take adverse action in your jurisdiction and for your purpose: your counsel, with the adverse action process page as background.
- The hosted consent flow and invitations that collect the consumer's authorization and e-mail, and the partner application itself.
- Reinvestigation outcomes and report revisions in detail: the disputes reference and the FCRA dispute API.
- Continuous checks that re-run a consented order on an interval, and the standard non-FCRA search endpoints.