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

# Document processing flow

> Walk through a complete document processing order: upload documents, create an order, retrieve results, and handle webhook updates.

This guide walks you through a typical workflow for a document processing order. With this workflow, document processing begins running after you upload documents to the KarmaCheck system and create the order. By the end of this guide, you'll know how to:

1. Authenticate with KarmaCheck.
2. Upload documents and create a secure document record for each one.
3. Order document processing by providing the candidate's PII and order data with the secure document record IDs.
4. Retrieve document processing results and receive status updates.

## Before you begin

Ensure that you have the following sandbox credentials:

* An API key
* A client access token

How you obtain these credentials depends on whether you're a [customer](/background-check-api/overview/customer-integration) or a [partner](/background-check-api/overview/partner-integration).

## Step 1: Authenticate with KarmaCheck

Call `POST /auth/api` to retrieve an authentication token. Replace `API_KEY` and `CLIENT_ACCESS_TOKEN` with your credentials:

```bash theme={null}
curl -X POST https://api-stage.karmacheck.io/auth/api \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "API_KEY",
    "clientAccessToken": "CLIENT_ACCESS_TOKEN"
  }'
```

The response body contains your authentication token in the `token` field. Use this as `Bearer AUTHENTICATION_TOKEN` in all subsequent requests.

## Step 2: Order document processing

### Step 2a: Upload your documents to KarmaCheck

This step references the full [Upload documents](/background-check-api/guides/upload-documents) guide. The key flow is:

1. **Request upload URLs** — Call `POST /document/secure/upload` to generate a secure, time-limited upload URL and `fileKey` for each document.
2. **Upload documents** — Upload each file directly to the returned URL using an HTTP `PUT` request with the correct `Content-Type`.
3. **Create secure document records** — Call `POST /document/secure/create` with the `fileKey`, `documentTypeId`, and `originalFileName`.

<Note>
  At this point you will not yet have a `caseId`. When no order exists yet, **omit the `accessGrants` object** from the request.
</Note>

4. **Note the `secureDocumentId`** returned for each document — you'll pass these when creating the order.

For request examples, supported MIME types, and error handling, see [Upload documents](/background-check-api/guides/upload-documents).

### Step 2b: Get available packages

Call `GET /package/min/list` to get a list of available packages:

```bash theme={null}
curl https://api-stage.karmacheck.io/package/min/list \
  -H "Authorization: Bearer AUTHENTICATION_TOKEN"
```

Find the package named **Intelligent Document Processing** and note its `id`.

### Step 2c: Get services in the package

Call `GET /package/id/{packageId}/services` to retrieve the services included in the package:

```bash theme={null}
curl https://api-stage.karmacheck.io/package/id/PACKAGE_ID/services \
  -H "Authorization: Bearer AUTHENTICATION_TOKEN"
```

Note the `serviceId` of the **Document Processing** service (`service-intelligent-document-process`). You'll need this when providing order data.

### Step 2d: Create a document processing order

Call `POST /case/create` to order document processing. The `orderData` array must include the minimally required candidate PII types plus one `codt-idp` entry per document:

```bash theme={null}
curl -X POST https://api-stage.karmacheck.io/case/create \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer AUTHENTICATION_TOKEN" \
  -d '{
    "packageId": "PACKAGE_ID",
    "email": "candidate@example.com",
    "givenName": "Jane",
    "familyName": "Smith",
    "postbackUrl": "POSTBACK_URL",
    "autoProcess": true,
    "candidateConsentProvided": true,
    "disableOptions": 7,
    "orderData": [
      {
        "caseOrderDataTypeId": "codt-govt-id",
        "metadata": { "govtId": "111-22-3333" }
      },
      {
        "caseOrderDataTypeId": "codt-dob",
        "metadata": { "dob": "1985-05-15" }
      },
      {
        "caseOrderDataTypeId": "codt-current-candidate-address",
        "metadata": {
          "address1": "1 Main Street",
          "address2": "Unit 179",
          "city": "Brooklyn",
          "state": "NY",
          "postalCode": "11201",
          "country": "US"
        }
      },
      {
        "caseOrderDataTypeId": "codt-idp",
        "serviceId": "service-intelligent-document-process",
        "metadata": {
          "documentTypeId": "unclassified",
          "fileName": "testfile1.pdf",
          "secureDocumentId": "SECURE_DOCUMENT_ID_1"
        }
      },
      {
        "caseOrderDataTypeId": "codt-idp",
        "serviceId": "service-intelligent-document-process",
        "metadata": {
          "documentTypeId": "unclassified",
          "fileName": "testfile2.pdf",
          "secureDocumentId": "SECURE_DOCUMENT_ID_2"
        }
      }
    ]
  }'
```

**`orderData` fields:**

| Field                            | Description                                                                         |
| -------------------------------- | ----------------------------------------------------------------------------------- |
| `codt-govt-id` / `govtId`        | Candidate's SSN in `xxx-xx-xxxx` format. Use `111-22-3333` for sandbox testing.     |
| `codt-dob` / `dob`               | Candidate's date of birth in `yyyy-mm-dd` format.                                   |
| `codt-current-candidate-address` | Candidate's current address (`address1`, `city`, `state`, `postalCode`, `country`). |
| `codt-idp` / `documentTypeId`    | Document type. Always use `unclassified` for Document Processing.                   |
| `codt-idp` / `fileName`          | The file name as provided when uploading the document.                              |
| `codt-idp` / `secureDocumentId`  | The ID returned after creating the secure document record.                          |

<Note>
  Set `disableOptions` to `7` to suppress KarmaCheck's candidate and customer notification emails for a document processing order. Because document processing is not a background-check screening, candidates should not receive the standard screening invitation or status-update emails. `disableOptions` is a bitmask that combines candidate invitation (`1`), candidate status updates (`2`), and customer notifications (`4`); a value of `7` disables all three. Omit the field or set it to `0` to keep notifications enabled.
</Note>

<Note>
  `postbackUrl` is for testing purposes only and supports only `case.statuschange` events. To configure webhooks for production, contact KarmaCheck to subscribe to webhook events.
</Note>

A successful response returns the `caseId` of the order. Note this value — you'll use it to retrieve results.

## Step 3: Retrieve document processing results

Call `GET /case/id/{caseId}/data/{serviceTypeId}` using `service-type-document-processing` as the `serviceTypeId`:

```bash theme={null}
curl https://api-stage.karmacheck.io/case/id/CASE_ID/data/service-type-document-processing \
  -H "Authorization: Bearer AUTHENTICATION_TOKEN"
```

The response returns a list of case data objects — one per document ordered. Key response fields:

| Field                              | Description                                                              |
| ---------------------------------- | ------------------------------------------------------------------------ |
| `status` / `statusDisplayName`     | Current processing status (e.g. `complete`).                             |
| `detailsObject.classificationData` | Document classification results including `type` and `confidence` score. |
| `detailsObject.value`              | Extracted data keyed by field name (e.g. `issueDate`, `dateOfBirth`).    |
| `detailsObject.metadata`           | OCR confidence scores and citation polygons for each extracted field.    |

**Example response:**

```json theme={null}
{
  "data": [
    {
      "id": "1777e943-7feb-42a9-aa35-2b73f781e4a8",
      "caseId": "8558c68f-5d39-4867-af25-57abeb047c5c",
      "serviceId": "service-intelligent-document-process",
      "serviceName": "Document Processing",
      "statusId": "case-data-status-complete",
      "status": "complete",
      "statusDisplayName": "Complete",
      "detailsObject": {
        "fileName": "US_PASSPORT-example.pdf",
        "documentTypeId": "us-passport",
        "secureDocumentId": "d17e3813-94e1-4717-8d92-df5e935af005",
        "documentExpirationDate": "2032-08-24 00:00:00",
        "classificationData": {
          "id": "classification_usC",
          "type": "us-passport",
          "confidence": 1
        },
        "value": {
          "issueDate": "2022-08-27",
          "dateOfBirth": "1990-09-25",
          "candidateName": "Avery Ellis",
          "expirationDate": "2032-08-24"
        }
      }
    }
  ]
}
```

## Step 4: Receive status updates (optional)

If you provided a `postbackUrl` when creating the order, KarmaCheck sends a `POST` request to that URL whenever the order status changes. Example webhook payload:

```json theme={null}
{
  "messageId": "e123e0d8-22dd-d45b-abc1-dddd5cd2a028",
  "event": "case.statuschange",
  "apiTrackingCode": null,
  "apiTrackingUser": null,
  "username": null,
  "password": null,
  "eventObject": {
    "id": "12345678-2e75-4ba7-abcde-06741f65df3e",
    "caseStatus": "Pending",
    "secondaryStatus": null,
    "resultType": "Open",
    "candidateEmail": "jane.smith@example.com",
    "candidateGivenName": "Jane",
    "candidateFamilyName": "Smith",
    "packageName": "Intelligent Document Processing"
  }
}
```

Respond with an HTTP `2xx` status to confirm receipt. If KarmaCheck does not receive a `2xx` response, it will retry the delivery after a delay. See the [webhooks overview](/background-check-api/reference/webhooks/webhooks-overview) for more details.

If you don't have a webhook set up, poll `GET /case/id/{caseId}` to retrieve the current status of the order.
