Skip to content

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.

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

The five states describe what you see from the outside. Behind each one:

  1. 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.
  2. 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.
  3. Independent review — every case gets a second, independent quality review before its results are finalized, regardless of outcome.
  4. 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.
  5. Results and delivery — once research and review are complete, results are published and the case reaches results_ready.
  6. 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.

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

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|allapi (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 (receivedin_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.

Each of these mirrors an endpoint or webhook above; the diagrams show them as a conversation between your system and RFR.

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)
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)
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}
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}
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
  • 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_ready and closed are the only signals you need for lifecycle purposes.
  • case.in_progress timing. 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 same interaction_id returns 400 rather than re-delivering the reply — see the REST endpoints table above.
  • Intake channel filter. GET /cases/ defaults to source=api — API-submitted cases only. Pass ?source=all to widen the list to every case visible to your organization, including cases submitted through other intake channels (e.g. Jira Service Management portal).