> For the complete documentation index, see [llms.txt](https://docs.bloodgpt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bloodgpt.com/submitting-tests/batch.md).

# Batch Processing

Submit multiple blood tests in a single API request

{% hint style="info" %}
Batch processing allows you to submit up to 100 blood tests in a single API call, with configurable error handling strategies.
{% endhint %}

## Overview

```mermaid
sequenceDiagram
    participant Client
    participant BloodGPT API
    participant Your Server

    Client->>BloodGPT API: POST /api/v1/batch (100 items)
    BloodGPT API-->>Client: 202 Accepted (batch_id)

    loop For each item
        BloodGPT API->>Your Server: Webhook (item_completed / item_failed)
    end

    BloodGPT API->>Your Server: Webhook (batch_completed / batch_partial / batch_failed)

    Client->>BloodGPT API: GET /api/v1/batch/{batch_id}/status
    BloodGPT API-->>Client: Batch status + item results
```

***

## Create Batch

**Endpoint:** `POST /api/v1/batch`

### Request

```json
{
  "on_error": "skip",
  "webhook_url": "https://your-server.com/webhooks/batch",
  "items": [
    {
      "external_id": "ext-001",
      "patient_id": "pat-001",
      "patient_info": {
        "birth_date": "1985-03-20",
        "gender": "male"
      },
      "payload": {
        "test_date": "2024-01-15",
        "parameters": [
          {
            "name": "Hemoglobin",
            "value": 14.5,
            "unit": "g/dL",
            "range": "12.0-16.0"
          },
          {
            "name": "Glucose",
            "value": 95,
            "unit": "mg/dL",
            "range": "70-100"
          }
        ]
      }
    },
    {
      "external_id": "ext-002",
      "patient_id": "pat-002",
      "file_url": "https://your-server.com/files/blood-test.hl7"
    },
    {
      "external_id": "ext-003",
      "patient_id": "pat-003",
      "file_url": "https://your-server.com/files/blood-test.pdf"
    }
  ]
}
```

### Parameters

| Field         | Type   | Required | Description                                      |
| ------------- | ------ | -------- | ------------------------------------------------ |
| `items`       | array  | Yes      | Array of test items (max 100)                    |
| `on_error`    | string | No       | Error handling strategy (default: `abort`)       |
| `webhook_url` | string | No       | Override webhook URL for all items in this batch |

### Item Fields

Each item in the `items` array supports the same fields as a single `/api/v1/process` request:

| Field                | Type   | Required    | Description                                |
| -------------------- | ------ | ----------- | ------------------------------------------ |
| `external_id`        | string | Yes         | Your unique identifier for this test       |
| `patient_id`         | string | Yes         | Patient identifier                         |
| `patient_info`       | object | No          | Patient demographics                       |
| `payload`            | object | Conditional | Structured test data (if not using file)   |
| `file_url`           | string | Conditional | URL to HL7/PDF file (if not using payload) |
| `processing_options` | object | No          | Language and other options                 |

***

## Error Handling Strategies

| Strategy | Behavior                                                               |
| -------- | ---------------------------------------------------------------------- |
| `abort`  | Validate all items first. Reject entire batch if any item is invalid.  |
| `skip`   | Accept valid items only. Invalid items are reported but not processed. |
| `defer`  | Accept all items. Validation errors discovered during processing.      |

### Example: Using `abort` (default)

If validation fails, you get a `400 Bad Request`:

```json
{
  "error": "batch_validation_failed",
  "message": "Batch validation failed with on_error=abort",
  "validation_errors": [
    {
      "index": 2,
      "external_id": "ext-003",
      "error": "missing_payload_or_file_url"
    }
  ]
}
```

### Example: Using `skip`

Valid items are accepted, invalid items are reported:

```json
{
  "batch_id": "clxyz789012",
  "status": "partial",
  "total_items": 10,
  "accepted_items": 8,
  "rejected_items": 2,
  "rejected": [
    {
      "index": 2,
      "external_id": "ext-003",
      "error": "missing_payload_or_file_url"
    },
    {
      "index": 7,
      "external_id": "ext-008",
      "error": "invalid_parameter_value"
    }
  ],
  "status_url": "/api/v1/batch/clxyz789012/status"
}
```

***

## Response (202 Accepted)

```json
{
  "batch_id": "clxyz789012",
  "status": "accepted",
  "total_items": 10,
  "accepted_items": 10,
  "rejected_items": 0,
  "status_url": "/api/v1/batch/clxyz789012/status"
}
```

***

## Get Batch Status

**Endpoint:** `GET /api/v1/batch/{batch_id}/status`

### Response

```json
{
  "batch_id": "clxyz789012",
  "status": "processing",
  "progress": {
    "total": 10,
    "accepted": 10,
    "rejected": 0,
    "completed": 6,
    "failed": 1,
    "pending": 3
  },
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:35:00Z",
  "completed_at": null,
  "items": [
    {
      "index": 0,
      "external_id": "ext-001",
      "test_id": "clxyz123456",
      "status": "completed",
      "download_url": "https://cdn.bloodgpt.com/reports/xxx.pdf?sig=..."
    },
    {
      "index": 1,
      "external_id": "ext-002",
      "test_id": "clxyz123457",
      "status": "completed",
      "download_url": "https://cdn.bloodgpt.com/reports/yyy.pdf?sig=..."
    },
    {
      "index": 2,
      "external_id": "ext-003",
      "test_id": "clxyz123458",
      "status": "processing"
    },
    {
      "index": 3,
      "external_id": "ext-004",
      "test_id": "clxyz123459",
      "status": "failed",
      "error": "Unable to process file"
    }
  ]
}
```

***

## Batch Status Values

```mermaid
stateDiagram-v2
    [*] --> pending
    pending --> validating
    validating --> processing
    processing --> completed: All items succeed
    processing --> partial: Some items failed
    processing --> failed: All items failed
    completed --> [*]
    partial --> [*]
    failed --> [*]
```

| Status       | Description                           |
| ------------ | ------------------------------------- |
| `pending`    | Batch created, processing not started |
| `validating` | Items being validated                 |
| `processing` | Items being processed                 |
| `completed`  | All items processed successfully      |
| `partial`    | Some items completed, some failed     |
| `failed`     | All items failed                      |

***

## Item Status Values

| Status       | Description                                       |
| ------------ | ------------------------------------------------- |
| `pending`    | Waiting to be processed                           |
| `processing` | Currently being analyzed                          |
| `completed`  | Successfully processed, `download_url` available  |
| `failed`     | Processing failed, `error` field contains details |
| `rejected`   | Failed validation (only with `on_error: skip`)    |

***

## Batch Webhooks

When you provide a `webhook_url`, BloodGPT sends two kinds of notification: one per item as it finishes, and one for the batch as a whole when all items are done. Each request carries the `X-BloodGPT-Signature` and `X-BloodGPT-Event` headers and is signed exactly like other [Webhooks](/interpretation-and-results/webhooks.md).

### Item events

Sent as each item completes or fails. `event` is `item_completed` or `item_failed`.

```json
{
  "event": "item_completed",
  "batch_id": "clxyz789012",
  "timestamp": "2024-01-15T10:35:00.000Z",
  "item": {
    "index": 0,
    "external_id": "ext-001",
    "test_id": "clxyz123456",
    "status": "completed"
  }
}
```

For a failed item, `event` is `item_failed`, `item.status` is `failed`, and an `item.error` string is included.

### Batch completion event

Sent once when the whole batch finishes. `event` is `batch_completed` (all items succeeded), `batch_partial` (some failed), or `batch_failed` (all failed).

```json
{
  "event": "batch_completed",
  "batch_id": "clxyz789012",
  "timestamp": "2024-01-15T10:40:00.000Z",
  "progress": {
    "total": 10,
    "completed": 10,
    "failed": 0
  }
}
```

{% hint style="info" %}
Use `batch_id` together with `item.index` / `item.external_id` to correlate notifications with items in your request. Item webhooks do not include a download URL — retrieve `download_url` for completed items from the [batch status endpoint](#get-batch-status).
{% endhint %}

***

## Complete Example

{% tabs %}
{% tab title="cURL" %}

```bash
# Create batch
curl -X POST https://sandbox.bloodgpt.com/api/v1/batch \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "on_error": "skip",
    "webhook_url": "https://your-server.com/webhooks/batch",
    "items": [
      {
        "external_id": "batch-test-001",
        "patient_id": "patient-123",
        "payload": {
          "test_date": "2024-01-15",
          "parameters": [
            {"name": "Hemoglobin", "value": 14.5, "unit": "g/dL", "range": "12-16"}
          ]
        }
      },
      {
        "external_id": "batch-test-002",
        "patient_id": "patient-456",
        "file_url": "https://your-server.com/files/test.hl7"
      }
    ]
  }'

# Check status
curl https://sandbox.bloodgpt.com/api/v1/batch/clxyz789012/status \
  -H "Authorization: Bearer YOUR_API_KEY"
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
const response = await fetch('https://sandbox.bloodgpt.com/api/v1/batch', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    on_error: 'skip',
    webhook_url: 'https://your-server.com/webhooks/batch',
    items: [
      {
        external_id: 'batch-test-001',
        patient_id: 'patient-123',
        payload: {
          test_date: '2024-01-15',
          parameters: [
            { name: 'Hemoglobin', value: 14.5, unit: 'g/dL', range: '12-16' }
          ]
        }
      }
    ]
  })
});

const { batch_id, status_url } = await response.json();

// Poll for status
const statusResponse = await fetch(`https://sandbox.bloodgpt.com${status_url}`, {
  headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const status = await statusResponse.json();
```

{% endtab %}
{% endtabs %}

***

## Limits

| Limit                  | Value      |
| ---------------------- | ---------- |
| Max items per batch    | 100        |
| Max file size per item | 40 MB      |
| Batch timeout          | 30 minutes |

***

## Best Practices

<details>

<summary><strong>Use webhooks instead of polling</strong></summary>

For large batches, use webhooks to receive notifications as each item completes. This is more efficient than polling the status endpoint.

</details>

<details>

<summary><strong>Choose the right error strategy</strong></summary>

* Use `abort` when all items must succeed together (e.g., regulatory submissions)
* Use `skip` when partial results are acceptable
* Use `defer` for best-effort processing with manual review later

</details>

<details>

<summary><strong>Track items by external_id</strong></summary>

Use meaningful `external_id` values to correlate batch results with your internal systems. The `batch_index` is 0-based and matches the order of items in your request.

</details>
