> ## Documentation Index
> Fetch the complete documentation index at: https://docs.truenroll.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Case lifecycle

> From submission to structured result: every stage a case travels through.

Every case submitted to TruEnroll travels through the same pipeline. A case can contain multiple uploads, each with one or more files. Stages run asynchronously and emit events as they complete, so you learn about each result as soon as it's ready.

## Pipeline overview

```mermaid theme={null}
flowchart TD
    U(["Submit documents"]) --> ING["Ingestion\nvalidate · store · create upload"]

    ING --> CLS{"Classify?"}

    CLS -->|yes| CLASS["Classification\nsplit into credentials"]
    CLS -->|no| EXT["Extraction\nidentity · institution · courses · grades"]

    CLASS --> EXT
    CLASS -.->|if requested| FOR["Forensics\ntamper & fraud detection"]
    CLASS -.->|if requested| TRN["Translation\nreadable representation"]

    EXT --> EVAL["Grade normalization\n& equivalence mapping"]

    EVAL --> DONE(["Structured result\navailable via API"])
    FOR --> DONE
    TRN --> DONE
```

<Info>
  Forensics and translation run **in parallel** with extraction, not after it. A case reaches terminal state only when every expected result (for every credential and every enabled feature) has completed or failed.
</Info>

## Stage by stage

<Steps>
  <Step title="Ingestion">
    Files are validated (format, size, count limits) and stored. A **case** is created (or an upload is added to an existing case if the `externalId` already exists) and processing is queued. The API responds immediately.

    At this point the upload is in `queued` status.
  </Step>

  <Step title="Ingestion">
    Files are validated (format, size, count limits) and stored. A **case** is created (or an upload is added to an existing case if the `externalId` already exists) and processing is queued. The API responds immediately with `202 Accepted`.

    At this point the upload is in `queued`/`processing` status.
  </Step>

  <Step title="Classification">
    TruEnroll analyses the uploaded files and splits them into **credentials** — one per education qualification identified. A single file can produce multiple credentials (for example, a transcript spanning two degrees). Non-education documents such as ID cards or cover letters are identified and excluded from further processing.

    Can be skipped by setting `classify: false` when submitting a case.

    **Webhook:** `case.classification.completed` / `case.classification.failed`
  </Step>

  <Step title="Classification">
    TruEnroll analyses the uploaded files and splits them into **credentials**, one per education qualification identified. A single file can produce multiple credentials (for example, a transcript spanning two degrees). Non-education documents such as ID cards or cover letters are identified and excluded from further processing.

    Can be skipped by setting `classify: false` when submitting a case.

    **Webhook:** `case.classification.completed` / `case.classification.failed`
  </Step>

  <Step title="Extraction">
    Structured fields are pulled from each education credential:

    | Field group        | Examples                                                         |
    | ------------------ | ---------------------------------------------------------------- |
    | Candidate identity | Full name, first/last name, date of birth                        |
    | Institution        | Name, country, state, accreditation status                       |
    | Qualification      | Degree name, level, major, year of admission, year of completion |
    | Courses            | Course number, title, credits, grade, grade point, marks         |
    | CGPA / totals      | CGPA, total credits or marks                                     |

    Extracted data is normalized into a consistent schema regardless of the source document's format or language.

    **Webhook:** `case.extraction.completed` / `case.extraction.failed`
  </Step>

  <Step title="Extraction">
    Structured fields are pulled from each education credential:

    | Field group        | Examples                                                         |
    | ------------------ | ---------------------------------------------------------------- |
    | Candidate identity | Full name, first/last name, date of birth                        |
    | Institution        | Name, country, state, accreditation status                       |
    | Qualification      | Degree name, level, major, year of admission, year of completion |
    | Courses            | Course number, title, credits, grade, marks                      |
    | CGPA / totals      | CGPA, total credits or marks                                     |

    Extracted data is normalized into a consistent schema regardless of the source document's format or language.

    **Webhook:** `case.extraction.completed` / `case.extraction.failed`
  </Step>

  <Step title="Grade normalization & equivalence">
    Extracted grades are interpreted against the issuing institution's grading scale (using TruEnroll's built-in scale library or a custom scale) and converted to target scales. Courses are mapped to CIP/SCED codes for domestic equivalence matching.
  </Step>

  <Step title="Forensics (parallel)">
    Running concurrently with extraction, the forensics engine checks the document for signs of tampering, manipulation, and internal inconsistency. See [Forensics & verification](/concepts/forensics-and-verification) for what's checked.

    **Webhook:** `case.forensics.completed` / `case.forensics.failed`
  </Step>

  <Step title="Translation (if enabled)">
    When the `translation` feature is requested, TruEnroll produces a page-structured, readable representation of the document in English. Each source file gets its own translation output, preserving page order and file identity.

    Translation runs in parallel with forensics and extraction — triggered after classification when `classify: true` (default), or immediately at upload time when `classify: false`.

    **Webhook:** `case.translation.completed` / `case.translation.failed`
  </Step>

  <Step title="Translation (if enabled)">
    When the `translation` feature is requested, TruEnroll produces a page-structured, readable representation of the document in English. Each source file gets its own translation output, preserving page order and file identity.

    Translation runs in parallel with forensics and extraction. It's triggered after classification when `classify: true` (default), or immediately at upload time when `classify: false`.

    **Webhook:** `case.translation.completed` / `case.translation.failed`
  </Step>
</Steps>

## Case status

A case moves through these states:

```mermaid theme={null}
stateDiagram-v2
    [*] --> queued: case created
    queued --> processing: pipeline starts
    processing --> completed: all results terminal, none failed
    processing --> failed: all results terminal, at least one failed
    completed --> [*]
    failed --> [*]
```

| Status       | Meaning                                          |
| ------------ | ------------------------------------------------ |
| `queued`     | Case created, waiting for pipeline to start      |
| `processing` | At least one result is still being processed     |
| `completed`  | Every expected result finished successfully      |
| `failed`     | All results are terminal and at least one failed |

<Note>
  A failed extraction does not necessarily make the whole case `failed`. If forensics and translation both succeed, and extraction is the only failure, the overall status depends on which features were requested. Every expected resource must reach a terminal state.
</Note>

## Partner API event timeline

This is what the lifecycle looks like from a partner integration's perspective.

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant P as Partner
    participant API as TruEnroll API
    participant Pipe as Processing pipeline
    participant WH as Webhook

    P->>API: POST /partner/v1/cases
    Note right of P: files + features + externalId
    API-->>P: 202 Accepted { caseId, upload }

    API->>Pipe: enqueue processing

    Pipe->>WH: case.classification.completed
    Pipe->>WH: case.extraction.completed
    P->>API: GET /partner/v1/cases/{id}/extraction

    Pipe->>WH: case.forensics.completed
    P->>API: GET /partner/v1/cases/{id}/forensics

    Pipe->>WH: case.translation.completed
    P->>API: GET /partner/v1/cases/{id}/translation
```

<Card title="Webhook reference" icon="bell" href="/partner-api/webhooks">
  Every event name, when it fires, and how to handle duplicates safely.
</Card>
