Case Lifecycle
Red Folder Research (RFR) runs two research products for law firms: Policy Limit, confirming known coverage and its limits, and Policy Existence, determining whether coverage exists at all. Every case you submit through the Partner API — regardless of product — moves through the same five-state contract: received, in_progress, action_required, results_ready, and closed.
That contract stays simple on purpose. Behind each state, our research team runs a consistent, quality-controlled process: intake validation, hands-on research by a specialist, and an independent review of every case before results go out. This page covers both halves — the state model your integration needs to handle, and enough of what happens behind it for the states to make sense.
The five states
Section titled “The five states”A case starts at received and, barring cancellation, ends at closed. The loop between in_progress and action_required can repeat more than once — every round where we need something from you re-enters research rather than starting a new case.
| State | Meaning |
|---|---|
received |
Queued; not yet in active research |
in_progress |
Research underway |
action_required |
We need information or documents from you before continuing |
results_ready |
Results are available |
closed |
Terminal — reached by full completion or by cancellation |
Behind the scenes
Section titled “Behind the scenes”The five states describe what you see from the outside. Behind each one:
- Intake — every submission goes through a completeness check before research starts. If something’s missing or malformed, you hear about it through the normal validation response rather than the case silently stalling.
- Research — a specialist works the case: confirming coverage and its limits for a Policy Limit request, or establishing whether coverage exists at all for a Policy Existence request.
- Independent review — every case gets a second, independent quality review before its results are finalized, regardless of outcome.
- An exhaustive second pass on Policy Existence no-hits — before we report that no coverage was found on a Policy Existence case, we run an additional, more exhaustive pass to confirm that conclusion holds up. A “no coverage found” result is a real, reviewed, billable answer — not a shortcut.
- Results and delivery — once research and review are complete, results are published and the case reaches
results_ready. - Closure — once delivery is confirmed (and, where applicable, payment completes), the case reaches
closed.
Loops are normal, not exceptions. When we need something from you, the case moves to action_required; once you respond, it re-enters research with a fresh SLA — the clock does not carry over stale time from before the round trip.
Webhook events
Section titled “Webhook events”| Event | Fires when | Notes |
|---|---|---|
case.received |
Case submitted, through any intake channel | Payload does not yet include issue_key |
case.files_updated |
You append a file via the API | Carries file_id + label |
case.in_progress |
Case enters active research | Consecutive duplicates are collapsed — it won’t fire twice in a row for the same case, but a return to in_progress after an intervening different event (e.g. action_required) fires again. Timing note: this can fire well after a specialist has actually started on the case — treat it as confirmation that research is underway, not as a start-time signal. A tightening of this timing is planned. |
case.action_required |
We need more information or documents from you | Carries message (8 KB cap) + interaction_id for /respond/. Only one request is open per case at a time; a case can go through multiple rounds over its lifetime |
case.results_ready |
Results are published, including regenerations (e.g. after previously offered policies are accepted) | Includes a fresh 12-hour presigned download URL each time; only re-fires when results actually change |
case.closed |
Full completion or cancellation | Fires exactly once per case; top-level reason field (e.g. results_delivered, a closing note, or a cancellation reason) |
case.additional_policies_found |
Additional policies are located during research | Includes offer_id and redacted teasers (price_usd, type); full policy values are revealed only after acceptance |
REST endpoints
Section titled “REST endpoints”All endpoints below are prefixed /api/v1/partner/, use token auth, and are scoped to your organization.
| Endpoint | Purpose | Lifecycle effect |
|---|---|---|
GET /cases/ |
Paginated case index. Supports ?source=api|all — api (default) limits the list to cases your integration submitted; all includes cases submitted through other intake channels for your organization |
— |
POST /cases/create/ |
One-shot create + submit (case JSON + files); returns 201 | Starts intake; issue_key is populated shortly after, not necessarily in the same response |
GET /cases/{ref}/ |
Status, metadata, and cancellation eligibility | — |
GET /cases/{ref}/results/ |
Results as PDF (presigned, 1 hour) or JSON | — |
GET /cases/{ref}/offers/ |
Latest additional-policy offer, if any | — |
POST /cases/{ref}/offers/accept/ |
Accept selected additional policies (consent recorded) | Triggers regenerated results with the accepted policies revealed; billing runs outside the API — see Integration notes |
POST /cases/{ref}/respond/ |
Answer an open action_required request (message and/or file) |
Delivers your reply to the case, then re-enters research (received → in_progress). Not safe to blind-retry: a repeat POST for the same interaction_id returns 400 (Interaction already in status: responded) rather than duplicating the reply — after a timeout, that 400 means your first attempt landed |
POST /cases/{ref}/files/ |
Append a file (25 files / 100 MB cap per case) | Fires case.files_updated; the file is attached to the case shortly after, not necessarily synchronously |
POST /cases/{ref}/cancel/ |
Cancel the case (free or fee tier depending on case state); returns 202 | Handles fee billing if applicable, then closes the case and fires case.closed |
Results JSON shape: {id, issue_key, status, results_data: {case_type, results[], additional_policies[]}, completed_at}. Offered-but-unaccepted policies are redacted to an envelope plus price; a cancelled case returns a cancellation block instead — see the Results page for the full schema.
The five two-way flows
Section titled “The five two-way flows”Each of these mirrors an endpoint or webhook above; the diagrams show them as a conversation between your system and RFR.
Case creation
Section titled “Case creation”sequenceDiagram
participant Y as Your system
participant A as RFR API
participant R as RFR research
Y->>A: POST /cases/create/ (case + files)
A-->>Y: 201 Created {case id} (issue_key not yet assigned)
A--)Y: webhook case.received
A->>R: case queued for intake
R->>R: intake validation, priority handling
Note over R: case picked up — research begins (state: in_progress)
Action-required round trip
Section titled “Action-required round trip”sequenceDiagram
participant Y as Your system
participant A as RFR API
participant R as RFR research
R->>R: research needs information from you — a specialist requests it
R->>A: request queued for delivery
A--)Y: webhook case.action_required {message, interaction_id}
Y->>A: POST /cases/{ref}/respond/ {interaction_id, message/file}
A->>R: your reply delivered to the case
Note over R: SLA resets — case re-enters research (state: received → in_progress)
Results delivery and closure
Section titled “Results delivery and closure”sequenceDiagram
participant Y as Your system
participant A as RFR API
participant R as RFR research
R->>R: research and independent quality review complete
R->>A: results published
A--)Y: webhook case.results_ready (12-hour presigned URL)
Note over R: invoicing runs outside this API — see Integration notes
R->>R: payment complete / delivery confirmed
A--)Y: webhook case.closed {reason: results_delivered}
Cancellation
Section titled “Cancellation”sequenceDiagram
participant Y as Your system
participant A as RFR API
participant R as RFR research
Y->>A: POST /cases/{ref}/cancel/
A-->>Y: 202 Accepted (free or fee tier, depending on case state)
A->>R: cancellation processed (fee handling if applicable)
R->>R: case closed
A--)Y: webhook case.closed {reason: partner-provided}
Additional policies (offer / accept)
Section titled “Additional policies (offer / accept)”sequenceDiagram
participant Y as Your system
participant A as RFR API
participant R as RFR research
R->>R: results reveal additional policies available for purchase
R->>A: offer ready
A--)Y: webhook case.additional_policies_found {offer_id, teasers}
Y->>A: GET /offers/ or POST /offers/accept/ (the firm may also accept directly)
A->>R: regenerate results with accepted policies revealed
A--)Y: webhook case.results_ready (new version)
Note over R: billing for accepted policies runs outside this API
Integration notes
Section titled “Integration notes”- Billing is off-contract. RFR bills by invoice, handled directly between our finance team and your organization’s billing contact — outside this API. There are no invoice or payment webhooks or endpoints today;
results_readyandclosedare the only signals you need for lifecycle purposes. case.in_progresstiming. This webhook can fire well after a specialist has actually started on the case. Treat it as confirmation that research is underway, not as a start-time signal.- Idempotency. The accept and cancel endpoints (
/offers/accept/,/cancel/) are safe to retry — repeating a call against the same case does not duplicate the underlying action./respond/is not: a repeat POST for the sameinteraction_idreturns 400 rather than re-delivering the reply — see the REST endpoints table above. - Intake channel filter.
GET /cases/defaults tosource=api— API-submitted cases only. Pass?source=allto widen the list to every case visible to your organization, including cases submitted through other intake channels (e.g. Jira Service Management portal).