M15 ships five pre-built REST endpoints that form a complete async pipeline: stage raw bytes, batch them into a persistent document record, wait for background processing, submit extraction jobs, and retrieve structured results. Here is exactly what each call expects and returns.
Endpoint paths are relative to their configured service base URLs. The media and PDF extraction services may have different base URLs, but both require the application's authenticated API client.
| Phase | Endpoint | Produces |
|---|---|---|
| 1 Stage file bytes | POST/media/stage-media-file-in-chunks | client-generated fileId, reused as stagedFileCode |
| 2 Create upload batch | POST/pdf-extraction/upload-document-batch | batchId (upload batch) |
| 3 Poll upload batch | GET/pdf-extraction/batch/{batchId} | blobId per uploaded item |
| 4 Submit extraction jobs | POST/pdf-extraction/submit-batch | batchId (extraction batch) |
| 5 Poll extraction batch | GET/pdf-extraction/batch/{batchId} | jobId per extraction item |
Render the CleenUI Dropzone and provide its onFileUpload callback. The callback generates a stable UUID for each file, splits the file into binary chunks, sends every chunk with its absolute byte offset, and reports upload progress through updateFile.
const CHUNK_SIZE = 4 * 1024 * 1024; // 4 MB chunks
const stageFile = async (fileEntry, updateFile) => {
const stagedFileCode = crypto.randomUUID();
const chunks = [];
for (let offset = 0; offset < fileEntry.file.size; offset += CHUNK_SIZE) {
chunks.push({
body: fileEntry.file.slice(offset, offset + CHUNK_SIZE),
offset,
});
}
await Promise.all(
chunks.map(async ({ body, offset }, index) => {
await mediaApi.stageMediaFileInChunks({
fileId: stagedFileCode,
offset,
chunk: body, // raw bytes · Content-Type: application/octet-stream
});
updateFile?.({ progress: (index + 1) / chunks.length });
})
);
return { ...fileEntry, id: stagedFileCode };
};
// Wire to the CleenUI Dropzone component:
<Dropzone
accept=".pdf,image/png,image/jpeg"
onFileUpload={stageFile}
showUploadedFiles
/>The staging endpoint stores bytes temporarily in memory only — no permanent record is created, no document is parsed, no extraction begins. The client owns the generated UUID; carry it alongside the selected file because it becomes stagedFileCode in step 2. Use a separate fileId per file and do not proceed until every chunk across every selected file has successfully uploaded.
After all files are staged, call this endpoint. It captures the temporary bytes, creates a persistent document batch, parses and encrypts each document in the background, stores successful files, and releases the staged in-memory bytes.
{
"items": [
{
"extractionTypeCode": "YOUR_EXTRACTION_TYPE",
"stagedFileCode": "78962f79-97d0-41a2-99ae-6eb758d13c5a",
"password": null,
"originalFilename": "TaxReturn2025.pdf",
"hints": null
}
]
}{
"batchId": "8a14dc31-c6cb-4ecd-a7bf-2e7d42530c14",
"status": "Pending",
"totalItems": 1
}extractionTypeCodeIdentifies the configured extraction workflow or document type.stagedFileCodeThe UUID generated and used as fileId while staging in step 1.passwordThe PDF password when required; null otherwise.originalFilenameUser-visible filename, including extension.hintsOptional extraction-specific metadata; null when not needed.Persist batchId — it is the key for polling in step 3. The API responds with 202 Accepted because processing continues asynchronously in the background.
Poll this endpoint until the batch reaches a terminal state. A typical client polls every eight seconds, stops polling when the view is abandoned, and enforces a maximum attempt count or overall timeout.
{
"batchId": "8a14dc31-c6cb-4ecd-a7bf-2e7d42530c14",
"batchType": "UPLOAD_DOCUMENTS",
"status": "Pending",
"totalItems": 1,
"completedItems": 0,
"succeededItems": 0,
"failedItems": 0,
"createdUtc": "2026-07-15T01:00:28.1055312",
"completedUtc": null,
"items": [
{
"index": 0,
"status": "Running",
"blobId": null,
"jobId": null,
"errorCode": null,
"errorMessage": null
}
]
}{
"batchId": "8a14dc31-c6cb-4ecd-a7bf-2e7d42530c14",
"batchType": "UPLOAD_DOCUMENTS",
"status": "Completed",
"totalItems": 1,
"completedItems": 1,
"succeededItems": 1,
"failedItems": 0,
"createdUtc": "2026-07-15T01:00:28.1055312",
"completedUtc": "2026-07-15T01:00:30.0046745",
"items": [
{
"index": 0,
"status": "Completed",
"blobId": 121387,
"jobId": null,
"errorCode": null,
"errorMessage": null
}
]
}PendingRunningCompletedCompletedWithErrorsThe batch-level status describes the aggregate state. Each item also carries its own status, so an item can be Running while the aggregate batch still reports Pending.
Only proceed to step 4 with items whose status is Completed and whose blobId is populated. For partial failures, surface the failed filename with its errorMessage and allow the user to retry or continue with the successful items.
After the upload batch completes, submit the extraction jobs. Create one request item per successfully uploaded file and copy that item's blobId into extractionUploadBlobId.
[
{
"responseSetId": 1025,
"extractionTypeCode": "YOUR_EXTRACTION_TYPE",
"forEntityTypeId": 56,
"forEntityId": 1025,
"hints": null,
"extractionUploadBlobId": 121387
}
]{
"batchId": "7cb27722-3e91-4e8b-8468-ed632bf719c2",
"status": "Pending",
"totalItems": 1
}responseSetIdThe response set that will receive the extracted values.extractionTypeCodeThe extraction workflow to execute. Must match the code used during upload.forEntityTypeIdThe type ID of the domain entity associated with this extraction job.forEntityIdThe ID of the specific domain entity.hintsOptional workflow-specific hints such as a known form type; null when not needed.extractionUploadBlobIdThe blobId from the completed upload item in step 3.This call produces a second, distinct batchId. The first tracked document upload and produced blobId values. This one tracks extraction-job creation and produces jobId values. Keep the two batch identifiers separate — they refer to different async processes.
Poll the same batch endpoint, this time with the extraction batchId. For this batch, batchType is SUBMIT_JOBS. A successfully completed item has a jobId. Pass each jobId to the status endpoint to retrieve the extracted field values.
| What the user sees | API condition |
|---|---|
| Upload progress | Chunks are being sent to stage-media-file-in-chunks |
| Processing | Either batch is Pending or Running |
| Review extracted values | Extraction jobs completed and returned extracted values |
| Partial failure | Batch is CompletedWithErrors; at least one item has errorCode |
| Apply / save | User confirmed the values; product persisted them to the backend |
These constraints separate a reliable document pipeline from one that silently drops files or duplicates submissions.
Use the application's authenticated API client. Do not place tokens in component code or Storybook stories.
Keep the file's stagedFileCode, upload-batch batchId, blobId, extraction-batch batchId, and jobId as distinct values.
Disable the Continue button until every selected file is fully staged across all chunks.
Cancel the polling cycle when the component unmounts or the user cancels the flow.
Treat both Completed and CompletedWithErrors as terminal. Do not wait for a state that will never arrive.
Only submit extraction jobs for items whose blobId is present and whose upload status is Completed.
Show errorCode and errorMessage per failed file. Do not discard successful items because one item failed.
Disable batch submission while a request or poll cycle is active.
Every endpoint, the chunked-upload React component, the batch-polling logic, and the C# controller code behind each call — all included in M15 on day one of your engagement.