Dossiers

Create a dossier from a case configuration, send the typed values it declares (loan amount, product, participants) and read back where the dossier stands. Documents are sent separately through workflow runs; both write to the same dossier.

Overview

Three steps: pick a configuration with GET /case-configs/, create the dossier with that config_id, then send documents with the dossier's case_id through workflow runs. The configuration decides which fields exist, which documents are expected and which checks run. Later calls address the dossier by case_id alone.

case_id identifies the dossier. Omit it and Holofin generates one (case_<random>), returned in the response. Or send your own reference (a loan number, a CRM id): the first call creates the dossier, later calls update it. Replaying a call is safe.

Values merge: only the keys you send are touched, an explicit null clears a key, and re-sending identical values changes nothing. Values are stored typed: send 250000, not "250000" (a numeric string is accepted and converted).

Read the exact payload your configuration expects from GET /case-configs/{config_id}, or open Show input schema in the dossier configuration screen for a copyable example.

The configuration is attached when the dossier is created and cannot be changed: sending the same config_id again is a no-op, a different one is refused with CONFIG_CONFLICT. To run the same case under another configuration, create another dossier. A dossier with no configuration (deleted, or created before config_id existed) adopts the one you name.

Create or update a dossier

POST /api/v1.1/cases

Push dossier data

curl -X POST https://app.holofin.ai/api/v1.1/cases \
  -H "X-API-Key: $HOLOFIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "config_id": "e5b1c9a0-4f2d-4b8e-9c3a-7d6f1e0a8b42",
    "case_fields": {
      "montant": 250000,
      "emprunteur": {
        "party_type": "PM",
        "firstname": "Marie",
        "lastname": "Durand",
        "siren": "552100554"
      }
    }
  }'
ParameterTypeRequiredDescription
case_idstringNoOmitted, Holofin generates one and returns it; that call always creates a new dossier. Sent, your own reference (at most 255 characters): creates the dossier on first use and identifies it afterwards. The same case_id on /workflow-runs/ names the same dossier.
config_idstring (uuid)On creationThe case configuration the dossier runs under, from GET /case-configs/. Required when the call creates the dossier (MISSING_CONFIG_ID), including when Holofin generates the case_id. Optional on an existing dossier: the same id passes, a different one is refused with CONFIG_CONFLICT.
case_fieldsobjectNoValues for the fields the configuration declares, as {"key": value}. A key naming a declared participant holds that participant's object instead (see below). An undeclared key is rejected (UNKNOWN_INPUT_FIELD). Omit it when the configuration declares nothing to send: config_id alone opens the dossier.

Participants

A participant is a key of case_fields named after its role, holding that participant's values. A role holding several participants takes a list of such objects.

The configuration declares a type of participant and how many the dossier expects: «borrower» one or two people, «guarantor» exactly one. The count is a completeness requirement, not a request validation: fewer participants than the minimum is accepted and the dossier stays incomplete until the rest arrive. The bounds are published under x-participant-cardinality in the configuration's schema.

  • Values merge into the participant under that key; it is created in the same call when it does not exist yet. Replaying a call is idempotent.
  • party_ref - Optional. Your own reference for the participant, the one party_refs on /workflow-runs/ names when a document is filed against them. Defaults to the field key.
  • party_ref is required when one key holds a list of several participants (FIELDS_WITHOUT_PARTY).
  • A participant type capped at one takes an object; a list under that key is rejected (INVALID_INPUT_FIELD).
  • party_type - Optional. The participant's legal nature: PM (personne morale), PP (personne physique), PPNS (personne physique non salariée) or BE (bénéficiaire effectif). Descriptive only: it does not decide which documents are expected. Rules read it as @case.<participant>.party_type. Kept when a later call omits it.
  • The same party_ref twice in one call is rejected (INVALID_PARTY_REF).

party_ref, role, party_refs and party_fields at the top level of the body are refused (UNSUPPORTED_FIELD). Participants are declared inside case_fields; party_refs belongs on /workflow-runs/, where it names the participants a document belongs to.

Sending documents

Send each document as its own POST /workflow-runs/ carrying the dossier's case_id. The dossier must exist first: a run never opens one. Holofin then works out which participant the document belongs to:

  • A dossier with a single participant files every document against that participant, so case_id alone is enough.
  • With several participants, a document is filed against the one its extracted identity designates, when the name read off it matches exactly one participant's declared identity.
  • A bundle the pipeline splits is attributed part by part, so one file can hold documents of several participants.

Send party_refs on the run when you know whose the file is. One reference files it against that participant; several declare a jointly held document, such as a joint account statement, that counts for each of them. On a bundle, party_refs names the file's owner: parts whose own content designates another participant still go to that one, and the rest fall back to your declaration.

A document nothing attributes (no usable name, or a name two participants share) counts toward no requirement. GET /cases/{case_id} lists it under unassigned_documents[] with reason set to awaiting_attribution.

Response

The response is the dossier as your call left it, re-evaluated against the configuration: the same body GET /cases/{case_id} returns, documented under The dossier body. case_id is the identifier you sent, or the one Holofin generated. A first call with no case_fields already answers the full requirement list, so one call tells you what to collect. Read case_status and the fields whose state is missing to know what to send next.

Read a dossier

GET /api/v1.1/cases/{case_id} returns the dossier: where it stands, what it still needs, and the verdict of every configured check. Instead of polling, subscribe to the case.updated and case.completedwebhook events: the same body is pushed on every status or coherence change.

curl https://app.holofin.ai/api/v1.1/cases/case_9f3k2m81xq7c \
  -H "X-API-Key: $HOLOFIN_API_KEY"

The dossier body

The body mirrors the configuration: the dossier's own fields and requirements at the root, participants keyed by role with each person's own fields and requirements, the configured checks in one flat rules list, and the documents counting toward nothing in unassigned_documents. Every document appears in exactly one place.

{
  "case_id": "case_9f3k2m81xq7c",
  "config_id": "6dc962d3-14df-4d41-b858-75f2b24b648f",
  "config_name": "Crédit professionnel",
  "case_status": "incomplete",
  "decision": "none",
  "coherence": "ko",
  "created_at": "2026-08-10T07:59:02Z",
  "evaluated_at": "2026-08-10T08:04:07.804604Z",
  "fields": {
    "montant": {"label": "Montant du prêt", "value": 250000, "state": "provided", "required": true},
    "duree": {"label": "Durée (mois)", "value": null, "state": "missing", "required": true}
  },
  "requirements": [
    {"key": "kbis", "label": "KBIS", "state": "satisfied",
     "min": 1, "max": null, "provided": 1,
     "accepted_categories": ["kbis"],
     "conditional": false, "activation": null,
     "documents": [
       {"document_id": "doc_k2VgTmN4pQ7cWxYz", "part_key": "",
        "state": "processed", "name": "kbis.pdf",
        "run_id": "wr_8sK2mQ4tXcV1nRy7",
        "category": "kbis", "pages": [1, 2], "owners": []}
     ]},
    {"key": "hote_identite", "label": "Pièce d'identité de l'hôte",
     "state": "not_required", "min": 1, "max": null, "provided": 0,
     "accepted_categories": ["piece_identite"],
     "conditional": true,
     "activation": {"active": false,
       "condition": "ASSERT @case.documents.justificatif_domicile.doc_category == 'attestation_hebergement'"},
     "documents": []}
  ],
  "participants": {
    "emprunteur": {
      "label": "Emprunteur", "state": "missing",
      "min": 2, "max": 2, "provided": 1,
      "parties": [
        {"party_ref": "REF-MARIE", "party_type": "PP",
         "fields": {
           "firstname": {"label": "Prénom", "value": "Marie", "state": "provided", "required": true},
           "lastname": {"label": "Nom", "value": null, "state": "missing", "required": true}
         },
         "requirements": [
           {"key": "releve_bancaire", "label": "Bank statement",
            "description": "Last two months, same account as the RIB",
            "state": "missing", "min": 2, "max": null, "provided": 1, "invalid": 1,
            "accepted_categories": ["releve_bancaire"],
            "conditional": false, "activation": null,
            "documents": [
              {"document_id": "doc_5rT9wLnB2xQjPmVd", "part_key": "",
               "state": "processed", "name": "releve_juin.pdf",
               "run_id": "wr_3fW7kZq9YtM5cHx2",
               "category": "releve_bancaire", "pages": [1],
               "owners": [{"party_ref": "REF-MARIE", "source": "declared"}]},
              {"document_id": "doc_8fRsLdH6wB3nJvQa", "part_key": "",
               "state": "invalid", "name": "releve_juillet.pdf",
               "run_id": "wr_6bN4pDs8RvK1mTy3",
               "category": "releve_bancaire", "pages": [1],
               "owners": [{"party_ref": "REF-MARIE", "source": "inferred"}]}
            ]}
         ]}
      ]
    }
  },
  "rules": [
    {"code": "case_rule_a3f19c04d2", "label": "Capital social suffisant",
     "kind": "consistency", "severity": "blocking", "status": "ko", "reason": null,
     "depends_on": [
       {"type": "requirement", "key": "kbis", "party_ref": null, "state": "ready"},
       {"type": "dossier", "key": "dossier", "party_ref": null, "state": "ready"}],
     "documents": [],
     "values": [
       {"field": "kbis.capital", "value": 5000,
        "document_id": "doc_k2VgTmN4pQ7cWxYz"},
       {"field": "dossier.montant", "value": 250000}],
     "provisional": false,
     "rule": "ASSERT @case.documents.kbis.capital >= @case.montant",
     "description": null},
    {"code": "case_rule_9d81b2c7e4", "label": "Relevé de moins de 3 mois",
     "kind": "validity", "severity": "blocking", "status": "ko", "reason": null,
     "depends_on": [
       {"type": "requirement", "key": "releve_bancaire",
        "party_ref": "REF-MARIE", "state": "ready"}],
     "documents": ["doc_8fRsLdH6wB3nJvQa"],
     "values": [], "provisional": false,
     "rule": "@releve_date > TODAY - 3 MONTH", "description": null},
    {"code": "case_rule_5c40aa91f8", "label": null,
     "kind": "consistency", "severity": "warning", "status": "skipped",
     "reason": "slot_inactive",
     "depends_on": [
       {"type": "requirement", "key": "hote_identite", "party_ref": null, "state": "inactive"},
       {"type": "requirement", "key": "kbis", "party_ref": null, "state": "ready"}],
     "documents": [], "values": [], "provisional": false,
     "rule": "WARN IF @case.documents.hote_identite.nom != @case.documents.kbis.dirigeant",
     "description": null}
  ],
  "unassigned_documents": [
    {"document_id": "doc_5tXcPeW9mK4dYbRu", "part_key": "",
     "name": "note_interne.pdf", "run_id": "wr_2hJ6gFp4LwS9qXc8",
     "category": "autre", "pages": [1], "owners": [],
     "reason": "category_not_accepted"},
    {"document_id": "doc_3wQhBnT7yL2xMcVe", "part_key": "segment_1",
     "name": "liasse.pdf", "run_id": "wr_7mV3cRt6NbD2kZw5",
     "category": "kbis", "pages": [1, 2], "owners": [],
     "reason": "requirement_full"},
    {"document_id": "doc_3wQhBnT7yL2xMcVe", "part_key": "segment_2",
     "name": "liasse.pdf", "run_id": "wr_7mV3cRt6NbD2kZw5",
     "category": "facture", "pages": [3], "owners": [],
     "reason": "category_not_accepted"}
  ]
}

Dossier

The root object. Each nested object is described in its own table below.

FieldTypeDescription
case_idstringThe dossier's reference: the one you sent, or the one Holofin generated.
config_id, config_namestringThe configuration the dossier runs under, as published by GET /case-configs/. null only on a dossier created before configurations were required.
case_statusstringincomplete (values, documents or participants missing, or a failed check), pending_processing, pending_review, rules_failed (a configured check could not run: a configuration problem, not something a document fixes), complete, or no_config (no configuration attached).
decisionstringThe reviewer's decision, separate from case_status: none, in_review, validated or rejected. Set in the Holofin app, read-only here.
coherencestringThe overall verdict of the configured checks: ok, ko or warning. null when there is no verdict (no rules configured, or nothing read yet). Read-only.
created_at, evaluated_atstringISO 8601 UTC. evaluated_at is the last evaluation, with microseconds.
fieldsobjectOne field per dossier-level field the configuration declares, keyed by field key. The keys are the ones case_fields accepts, so the missing entries are what to send next.
participantsobjectOne role per participant type the configuration declares, keyed by role, present even when nobody fills it yet.
requirementsarrayThe dossier-level expected documents, one requirement each. Participant-owned requirements sit under their party instead.
rulesarrayOne flat list of the configured checks and their current verdicts, one rule each, wherever they attach.
unassigned_documentsarrayThe units counting toward nothing: document rows with a reason. An empty list means everything you sent counts.

Field fields.<key>

FieldTypeDescription
labelstringThe configured name. null when the configuration no longer declares the field.
valueanyThe current merged value, coerced to its declared type. null when absent.
statestringprovided, missing (required and absent: the dossier stays incomplete until it arrives) or empty (not required and absent).
requiredbooleanWhether the configuration demands the field as of the last evaluation. A conditionally required field may flip as the dossier fills in.

Role participants.<role>

FieldTypeDescription
labelstringThe configured name of the participant type.
statestringok, missing (below the minimum) or exceeded (above the maximum).
min, maxintegerHow many people the dossier expects. max is null when uncapped.
providedintegerHow many people currently fill the role.
partiesarrayAlways a list, one party per person.

Party parties[]

FieldTypeDescription
party_refstringYour reference for the person, the one party_refs on /workflow-runs/ names. null when none was sent.
party_typestringThe legal nature (PM, PP, PPNS, BE), or null.
fieldsobjectThis person's values, same shape as the dossier's fields.
requirementsarrayThe requirements this person owns. A participant-owned requirement appears only here.

Requirement requirements[]

A requirement owned by a participant the dossier does not hold yet has no row; it appears under the person once they are added.

FieldTypeDescription
key, labelstringThe requirement's key and configured name.
descriptionstringThe configuration's own explanation of the requirement. Omitted when none.
statestringsatisfied, pending (enough documents received, some still processing), missing, exceeded (more counting documents than max) or not_required (a conditional requirement whose condition is not met).
min, maxintegerHow many counting documents are expected. max is null when uncapped; min: 0 is an optional requirement.
providedintegerDocuments processed and valid.
invalid, in_flight, failedintegerDocuments that did not hold up, are still processing, or failed. Omitted when zero.
accepted_categoriesarrayThe document types that satisfy the requirement.
groupstringPresent only when the configuration sets one.
conditionalbooleantrue for a requirement that only applies when a condition holds.
activationobjectnull for a plain requirement. Otherwise {"active", "condition"}: active says whether the condition currently holds, condition is the configured rule text (several lines mean any one of them activates), null unless rule details are enabled for your organization.
documentsarrayThe document rows counting under this requirement. There is no separate flat list.

Document row documents[] · unassigned_documents[]

FieldTypeDescription
document_id, part_keystringThe unit's identity. part_key is "" for a whole file; a bundle the pipeline split has one row per part, materialized when processing completes. The pair joins to the workflow run's checks.
statestringprocessed, in_flight, failed, or invalid (failed the requirement's validity rules; the row stays listed).
namestringThe filename, repeated on every part of a split bundle.
categorystringThe document type the unit is filed as, in the accepted_categories vocabulary. null until classified.
pagesarrayThe unit's page numbers in the file, 1-based, as the workflow run numbers them. null until the file is paged.
run_idstringThe workflow run that produced the document, joining to GET /workflow-runs/{run_id} for the full extraction and document-level check verdicts. null when unknown.
ownersarray[{"party_ref", "source"}], empty for a dossier-level document. source is declared (your party_refs on the upload), manual (a reviewer's choice, which outranks a declaration) or inferred (Holofin matched the document's content to a participant). A joint document lists several owners.
sharedbooleanPresent when a shareable requirement counts the unit without consuming it; the unit also appears under the requirement that seats it. Omitted otherwise.
duplicate_ofstringThe document this one was deduplicated against. Omitted otherwise.
reasonstringunassigned_documents only. not_yet_classified (no category yet, usually still processing), category_not_accepted (no requirement accepts its category), awaiting_attribution (a participant-owned requirement accepts its category, but the dossier holds several participants and nothing says whose document it is: send party_refs on the workflow run; never reported on a single-participant dossier) or requirement_full (capacity reached, wrong participant, or failed extraction; a document belonging to one participant reads this against another participant's requirement, even one still missing).

Rule rules[]

Every key is always present.

FieldTypeDescription
codestringStable identifier derived from the rule's text; track a check by it, not by matching the text. Editing the rule changes its code.
labelstringThe configured name, as shown in the Holofin app. null for a rule written without one.
kindstringvalidity (judges one requirement's documents) or consistency (compares values across documents and declared data). Conditional-requirement activation is not a rule entry; it sits on the requirement's activation.
severitystringblocking or warning, as configured. A blocking failure holds the dossier (coherence: "ko", case_status: "incomplete"); a warning blocks nothing.
statusstringok, ko or skipped. A failing warning reads ko with severity: "warning". skipped means the check never ran (nothing yet in the requirements it reads) and is excluded from coherence.
reasonstringfield_missing on a ko whose referenced value is absent; on a skip, no_instances, slot_inactive, no_applicable_parties or aggregate_empty. null otherwise.
depends_onarrayWhat the check reads, each as {"type", "key", "party_ref", "state"}. type is requirement, participant or dossier. party_ref names the person for a participant-scoped entry, null at dossier level; a check reading a participant is evaluated once per person, under the same code. state is ready, pending (a document still processing), missing (no document counted, or nobody fills the participant) or inactive (a conditional requirement not activated). A validity check lists the requirement it judged first.
documentsarrayFor a validity check, the documents it rejected (the ones reading state: "invalid" under their requirement). Empty otherwise.
valuesarrayThe observed values the verdict was decided on: field, value and, when read from a document, document_id. An aggregate carries its function under aggregate; an array field is reported by length (list: true); a value that could not be read carries missing: true. Present on passing checks too.
provisionalbooleantrue on a ko that only waits for a registry lookup; the dossier re-evaluates by itself when it lands.
rule, descriptionstringThe rule's text, and what the check verifies in plain language. Both null unless rule details are enabled for your organization.

Dossier activity

GET /api/v1.1/cases/{case_id}/activities returns the dossier's change trail: every recorded change, newest first, including what a reviewer changed after you sent it. The trail is not embedded in the dossier body.

Read the trail

curl "https://app.holofin.ai/api/v1.1/cases/case_9f3k2m81xq7c/activities?since=2026-09-01T00:00:00Z" \
  -H "X-API-Key: $HOLOFIN_API_KEY"
{
  "case_id": "case_9f3k2m81xq7c",
  "activities": [
    {"id": "f3887775-2a65-4f60-a8df-d3388fc3148e",
     "at": "2026-09-01T20:33:33Z", "type": "extraction.corrected",
     "actor": {"kind": "user", "name": "Charles Prime"},
     "participant": {"key": "emprunteur", "party_ref": "REF-MARIE"},
     "document": {"document_id": "doc_8fRsLdH6wB3nJvQa", "part_key": "",
                  "name": "releve_juillet.pdf", "run_id": "wr_6bN4pDs8RvK1mTy3", "page": 1},
     "summary": "Field \"titulaire\" corrected",
     "changes": [{"field": "titulaire", "before": "DUPONT M.", "after": "DUPONT Marie"}],
     "note": null, "operation": "update"},
    {"id": "0c2d7e4a-9b1f-4c63-8e5a-2f7d1b9c4e10",
     "at": "2026-09-01T20:31:05Z", "type": "owner.changed",
     "actor": {"kind": "system", "name": null},
     "participant": {"key": "emprunteur", "party_ref": "REF-MARIE"},
     "document": {"document_id": "doc_3wQhBnT7yL2xMcVe", "part_key": "segment_2",
                  "name": "liasse.pdf", "run_id": "wr_7mV3cRt6NbD2kZw5"},
     "summary": "Owner attributed",
     "changes": [{"field": "owners", "before": [],
                  "after": [{"party_ref": "REF-MARIE", "source": "inferred"}]}],
     "note": "Matched on the document's identity fields."},
    {"id": "6a1e0b3d-4f52-4c8b-9d7e-1b2c3d4e5f60",
     "at": "2026-09-01T20:30:58Z", "type": "status.changed",
     "actor": {"kind": "system", "name": null},
     "participant": null, "document": null,
     "summary": "Dossier went from pending_processing to incomplete",
     "changes": [{"field": "case_status", "before": "pending_processing", "after": "incomplete"}],
     "note": "aggregator_terminal"},
    {"id": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
     "at": "2026-09-01T20:29:41Z", "type": "field.changed",
     "actor": {"kind": "api", "name": null},
     "participant": {"key": "emprunteur", "party_ref": "REF-MARIE"},
     "document": null,
     "summary": "2 fields changed",
     "changes": [{"field": "firstname", "before": null, "after": "Marie"},
                 {"field": "lastname", "before": null, "after": "DUPONT"}],
     "note": null}
  ],
  "pagination": {"page_size": 100, "has_next": false}
}

Activity entry

FieldTypeDescription
idstringStable per entry; deduplicate on it between polls.
atstringISO 8601 UTC. The ordering key.
typestringThe kind of change, object.verb. See the table below.
actorobject{"kind", "name"}. kind is user (a person in the Holofin app, by display name, never an email), api (your integration; no person is named) or system (the pipeline). null when nothing recorded who.
participantobject{"key", "party_ref"} when the change is about one person; null for a dossier-level change or a jointly owned document.
documentobject{"document_id", "part_key", "name", "run_id"} when the change is on a document, plus page on an extraction correction; null otherwise. Same identity as the document rows of the dossier body.
summarystringAn English sentence for a log line. May be reworded.
changesarray{"field", "before", "after"} entries, empty when there is nothing to diff. before is the value just before this change: on an extraction, the previous correction's value, not the pipeline's original reading. On owner.changed the values are owner lists in the body's owners shape; on status.changed and decision.* they are the status words the body uses.
notestringThe comment's text, the decision's reason, the attribution's justification or the evaluation trigger; null otherwise.
operationstringextraction.corrected only: update, add, delete or restore.

Activity types

TypeDescription
field.changedA declared value set or edited, on the dossier or on a participant. actor.kind: "user" means a reviewer changed what you sent.
party.createdA participant added.
document.addedA document added.
extraction.correctedA reviewer edited a value read from a document.
owner.changedA document part's owners: attributed by the pipeline, overridden by a reviewer, reset or removed.
decision.takenA reviewer took a decision.
decision.reopenedA decision voided. When a re-evaluation does it, the actor is the pipeline and note says what moved.
comment.addedA note on the dossier or on a document; note holds the text.
status.changedA case_status transition, with the trigger in note (link_added, extraction_revalidated, config_saved…).

A document unlinked or a participant deleted is not recorded as such; only the status.changed entry of its re-evaluation appears.

Query parameters

ParameterTypeDescription
typestringOptional. Comma-separated activity types to keep, e.g. field.changed,extraction.corrected. An unknown type is refused with INVALID_FILTER.
sincestringOptional. ISO 8601 datetime. Keeps entries stamped at or after that instant (inclusive), so a poller re-sends the newest at it saw and deduplicates on id.
limitintegerOptional. Page size, 1 to 100. Defaults to 100; larger values are clamped.
next_tokenstringOptional. The pagination.next_token of the previous response, to fetch the next page.

Pagination is the same cursor block as the dossier listing: page_size, has_next, and next_token when there is a next page. Send it back as next_token with the same filters until has_next is false. A token that does not decode is refused with INVALID_FILTER; an unknown query parameter with UNKNOWN_PARAMETER.

List dossiers

GET /api/v1.1/cases returns one row per dossier of your organization, with no terminal-state restriction: a dossier still being assembled is listed like any other. Use it to reconcile your own records and to find what changed since your last pass.

Rows carry the status columns only. Requirements, documents and rule verdicts come from GET /cases/{case_id}.

A dossier with no case_id is not listed.

List dossiers

curl "https://app.holofin.ai/api/v1.1/cases?updated_after=2026-08-20T00:00:00Z" \
  -H "X-API-Key: $HOLOFIN_API_KEY"
{
  "cases": [
    {"case_id": "L-2026-0417",
     "case_status": "complete",
     "completion": true,
     "coherence": "ok",
     "decision": "validated",
     "created_at": "2026-08-21T09:12:44.318921Z",
     "evaluated_at": "2026-08-22T16:03:11.207845Z"},
    {"case_id": "case_9f3k2m81xq7c",
     "case_status": "incomplete",
     "completion": false,
     "coherence": "ko",
     "decision": "in_review",
     "created_at": "2026-08-20T14:51:02.664310Z",
     "evaluated_at": "2026-08-20T14:51:02.981204Z"}
  ],
  "pagination": {
    "page_size": 100,
    "has_next": true,
    "next_token": "MjAyNi0wOC0yMFQxNDo1MTowMi42NjQzMTArMDA6MDB8YzFkNGY2ODAtOWEyMy00ZTdmLThiMzUtMmY0YTZkOTBjMTdl"
  }
}

Listing row

FieldTypeDescription
case_idstringThe dossier's reference, the one you send to GET /cases/{case_id} and to /workflow-runs/.
case_status, coherence, decisionstringThe same values as the dossier body. coherence is null when there is no verdict.
completionbooleantrue exactly when case_status is complete.
created_atstringISO 8601 UTC. The ordering key.
evaluated_atstringWhen the last evaluation ran; null for a dossier never evaluated.

Filters combine. An unsupported query parameter is refused (UNKNOWN_PARAMETER).

ParameterTypeDescription
case_statusstringOptional. One of no_config, incomplete, pending_processing, pending_review, rules_failed, complete. Case-insensitive.
decisionstringOptional. One of none, in_review, validated, rejected. Case-insensitive.
coherencestringOptional. One of ok, ko, warning, review, none. Case-insensitive. review is reserved and currently matches nothing. none selects the dossiers whose row answers "coherence": null.
updated_afterstringOptional. ISO 8601 datetime. Only dossiers last modified at or after it. Filters on the last modification, not created_at, so a dossier created before your last pass whose status has moved since is included.
updated_beforestringOptional. ISO 8601 datetime. Only dossiers last modified at or before it. Both bounds are inclusive.
limitintegerOptional. Rows per page. Default 100, also the maximum; a value outside 1 to 100 is clamped. A non-integer is refused.
next_tokenstringOptional. The pagination.next_token of the previous response, to fetch the next page.

Pagination is cursor-based. pagination carries page_size (after clamping), has_next, and next_token when there is a next page. Send it back as next_token with the same filters until has_next is false. The token is opaque. Rows come newest first by creation, so page boundaries stay stable while dossiers are updated.

A token that does not decode is refused with INVALID_FILTER, and so is a token issued by the workflow runs listing. This is stricter than the workflow runs listing, which starts over from the first page on a broken token.

Errors

On POST /cases everything is validated before anything is written; a rejected call never leaves a half-populated dossier behind.

CodeStatusMeaning
INVALID_CASE_ID400A case_id was sent but is not a non-empty string of at most 255 characters. Omit it to have one generated.
MISSING_CONFIG_ID400The call creates the dossier and carries no config_id. List your configurations with GET /case-configs/.
UNKNOWN_CONFIG400The config_id names no configuration of your organization (a missing configuration, a non-UUID id and another organization's configuration answer the same way).
CONFIG_CONFLICT409The config_id names a different configuration than the dossier already runs under. Create another dossier to use another configuration.
UNSUPPORTED_FIELD400A top-level party_ref, role, party_refs or party_fields. Send participants inside case_fields.
UNKNOWN_INPUT_FIELD400A key is not declared by the case configuration. The message lists the declared keys.
INVALID_INPUT_FIELD400A value does not match its declared type: a non-numeric amount, a date that is not YYYY-MM-DD, a value outside an enum's list, a SIREN failing its checksum, or a list under a participant key capped at one.
FIELDS_WITHOUT_PARTY400One key holds several participants and one of them carries no party_ref.
INVALID_PARTY_REF400A party_ref is malformed, or the same reference appears twice in one call.
INVALID_PARTY_TYPE400A party_type outside PM, PP, PPNS, BE.
UNKNOWN_ROLE400A participant key is not defined by the case configuration. The message lists the allowed roles.
UNKNOWN_PARAMETER400On the listing and activities, a query parameter the endpoint does not support. The message names it and lists what is supported.
INVALID_FILTER400On the listing and activities, a supported parameter with an unusable value: a status outside its list, a datetime that is not ISO 8601, a non-integer limit, an unknown activity type, or a next_token that is not a valid cursor.
APPLICATIONS_DISABLED400The Dossiers feature is not enabled for your organization.
NOT_FOUND404No dossier with that case_id in your organization.