> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://cla-demo.postmansolutions.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://cla-demo.postmansolutions.com/_mcp/server.

# Process Flow

## Where is Document-splitter service in the high level process flow?

[https://lucid.app/lucidchart/c0c23b5d-5787-496c-afff-dafb31d6f693/edit?invitationId=inv\_21daa08b-cb9b-4082-b27d-6c68118994d6\&page=0\_0#](https://lucid.app/lucidchart/c0c23b5d-5787-496c-afff-dafb31d6f693/edit?invitationId=inv_21daa08b-cb9b-4082-b27d-6c68118994d6\&page=0_0#)

## Document Splitter Event flow

```
Azure Service Bus                Document Splitter                
─────────────────                ─────────────────                
                                 ┌─────────────────┐
classified-file topic ──────────►│ messageReceiver │
                                 └────────┬────────┘
                                          │ routes to handler
                                          ▼
                                 ┌─────────────────────────────┐
                                 │   classifiedFileHandler     │
                                 │                             │
                                 │ 1. validate event fields    │
                                 │ 2. create job (SQL)         │
                                 │ 3. download classification ─┼────────────────────► [GET classifiedOutputUrl]
                                 │ 4. download source PDF ─────┼────────────────────► [GET internalUrl]
                                 │ 5. validate page ranges     │
                                 │ 6. split PDF (pdf-lib)      │
                                 │ 7. upload split files ──────┼──── HTTP POST ──────► File Intake Service
                                 │    (parallel)               │                           │
                                 │                             │◄── blob URLs ─────────────┘
                                 │ 8. publish split-file ──────┼──► split-file topic (one per file)
                                 │    events                   │
                                 │ 9. upload manifest JSON ────┼────────────────────► [PUT splits container]
                                 │ 10. publish result event ───┼──► split-file-result topic
                                 │ 11. complete job (SQL)      │
                                 └─────────────────────────────┘
                                          │ on any error
                                          ▼
                                 problem topic ──────────────────► Status / Alert service
```

## On Every Incoming Message

### Step 1 , Receive and validate the event

* Deserialises the `FileClassifiedEvent` from the Service Bus message body
* Validates required fields: `applicationId`, `clientApplicationId`, `clientApplicationClassifierId`, `fileId`, `internalUrl`, `classifiedOutputUrl`
* Rejects messages with missing fields and publishes a `ProblemEvent` (HTTP 400)

### Step 2 , Create a job record in SQL

* Inserts a row into `[splitter].[Job]` with `status = 'Started'`
* If the job already exists (retry scenario), upserts it
* Stores the original `fileId` for correlation

### Step 3 , Download the classification JSON

* Parses the `classifiedOutputUrl` to extract container name and blob path
* Downloads the JSON file from Azure Blob Storage
* Deserialises it as a `ClassificationResult`:
  * `is_multi_form` , whether the PDF contains multiple forms
  * `num_pages` , total page count of the PDF
  * `forms[]` , array of form objects, each with `form_type`, `page_start`, `page_end` (or `page_ranges[]` for unknown form types), `confidence`

### Step 4 , Download the source PDF (parent file)

* Parses `internalUrl` to extract container name and blob path
* Downloads the PDF as a raw binary buffer from Azure Blob Storage

### Step 5 , Validate page ranges

Ensures the classification metadata is coherent before touching the PDF:

| Check                                             | Reason                                                    |
| ------------------------------------------------- | --------------------------------------------------------- |
| PDF page count matches `num_pages`                | Prevents silent data loss if the wrong PDF was referenced |
| All `page_start` / `page_end` values are integers | Guards against malformed classifier output                |
| All page numbers are within `1 … num_pages`       | Out-of-range pages would cause `pdf-lib` to throw         |
| `page_start <= page_end` for every form           | Catches inverted ranges                                   |
| No overlapping page ranges between forms          | Each page should belong to exactly one form               |

If any check fails → publish `ProblemEvent` (HTTP 400) and set job status to `Failed`.

### Step 6 , Split (or pass through) the PDF

> **Multi-form documents (`is_multi_form = true`):**
>
> * For each form in the classification:
> * Extracts the specified pages using `pdf-lib`
> * Saves the result as an in-memory `Buffer`
> * Generates a filename: `{timestamp}_{baseName}_pg{start}-{end}_{safeFormType}.pdf`
> * Sanitises `form_type` (replaces any character that is not alphanumeric, `-`, or `_` with `-`)

> **Single-form documents (`is_multi_form = false`):**
>
> * No splitting is performed
> * The original PDF buffer is used as-is
> * Defaults `page_start = 1`, `page_end = num_pages`

> **Extra / unknown form types:**
>
> * Some forms have `page_ranges[]` instead of a single `page_start` / `page_end`
> * The splitter merges those non-contiguous page ranges into a single PDF output

### Step 7 , Upload each split file via the File Intake service

* HTTP `POST` to `{FILE_INTAKE_BASE_URL}/v1/upload/{applicationId}?parentFileId={fileId}&clientApplicationClassifierId={classifierId}`
* Body: `multipart/form-data` with the PDF buffer
* The file intake service:
  * Assigns a new child `fileId`
  * Stores the file in blob storage
  * Returns a blob URL and file metadata
* All uploads happen in parallel (`Promise.allSettled`)
* Any single upload failure is captured, it will send a Problem event ; it does not cancel other uploads. If one or multiple files encounter an error, the Job will result to Failed.

### Step 8 , Publish one `FileSplitFileEvent` per split file

* Published to the `split-file` topic on Azure Service Bus
* Contains: `clientApplicationId`, the new blob URL (`internalUrl`), `fileLocationUrl`, `timestamp`
* Downstream extractors subscribe to this topic and know to process each message as one form

### Step 9 , Build and upload the split manifest

* Generates a JSON document listing all split results:
  * Per item: `fileName`, `formType`, `formIndex`, `pageStart`, `pageEnd`, `blobUrl`
* Uploads to the `splits` blob container at path:
  * `{clientApplicationId}/splitResults/{clientApplicationClassifierId}/{timestamp}_{baseName}_splitResult.json`

### Step 10 , Publish the `FileSplitFileResultEvent`

* Published to the `split-file-result` topic
* Contains: `clientApplicationId`, `fileId`, `parentFileBlobUrl`, `classificationUrl`, `totalSplitFiles`, `splitFiles[]`, `timestamp`
* Gives consumers a single message summarising the entire split operation

### Step 11 , Update job status in SQL

* Updates the row in `[splitter].[Job]` to `status = 'Completed'`
* Stores `splitManifestBlobUrl` (the URL of the manifest uploaded in step 9)
* Sets `completedAt` timestamp

## On Any Error

| Scenario                               | Action                                                                                |
| -------------------------------------- | ------------------------------------------------------------------------------------- |
| Missing required event fields          | Publish `ProblemEvent` (status 400), set job `Failed`                                 |
| Blob not found (PDF or classification) | Publish `ProblemEvent` (status 404), set job `Failed`                                 |
| Invalid page ranges in classification  | Publish `ProblemEvent` (status 400), set job `Failed`                                 |
| Upload failure for a split file        | Captured in `allSettled` result; partial success still proceeds                       |
| Unhandled / unexpected error           | Publish `ProblemEvent` (status 500), set job `Failed`, re-throw for Service Bus retry |
| All retries exhausted                  | Service Bus dead-letters the message for manual inspection                            |