# Test Results

Structured or file-based Test Results.

## Create up to 100 Test Results in one request

`POST /v1/test_result_batches`

Returns `201` when every TestResult is created immediately. Returns `202` when at least one item includes `file_url` or `file_base64`; poll `remote_file_import_batch` for each file's import status. Both responses use `TestResultBatchResponse`.

**Required API scopes:** `test_results.create`

## Parameters

- `Idempotency-Key` (header, `string`) (required) - Client-generated idempotency key. Required for every POST/PATCH write. Replay of the same key with the same body returns the stored response with an `Idempotency-Replayed: true` header. Same key + different body returns `422 idempotency_key_reused`. A duplicate that arrives while the first request is still in flight returns `409 idempotency_conflict` with `Retry-After: 1`.

## Request body (`application/json`)

- `object`
  - `items` (`array`) - The test results to create. Items are processed in their submitted order.
    - `items` (`object`)
      - `automatically_create_resource_permission_for_patient` (`boolean`) - Whether Carebit should automatically share the test result with the patient after processing.
      - `booking_id` (`string | null`) - format: `uuid`; The identifier of the booking associated with the test result, or null when it is not linked to a booking.
      - `clinician_id` (`string | null`) - format: `uuid`; The identifier of the clinician associated with the test result, or null when none is assigned.
      - `description` (`string | null`) - A description of the TestResult.
      - `file_base64` (`string | null`) - format: `byte`; The test result bytes encoded as Base64. Provide this with `filename` instead of `file_url`. The decoded file can be at most 7 MB.
      - `file_url` (`string | null`) - format: `uri`; The public HTTPS URL of a test result document that Carebit should download.
      - `filename` (`string | null`) - The filename to use for the test result. Required with `file_base64`; defaults to the remote file's filename for URL sources.
      - `notify_patient_of_resource_permission` (`boolean`) - Whether Carebit should notify the patient when the test result is shared with them.
      - `patient_id` (`string`) - format: `uuid`; The identifier of the patient that the test result belongs to.
      - `status` (`string`) - enum: `awaiting_review`, `complete`, `draft`, `reviewed`; The workflow status to assign to the test result.
      - `test_result_items` (`array`) - The structured clinical observations to include in the test result.
        - `items` (`object`)
          - `is_abnormal` (`boolean | null`) - Whether the observation falls outside its reference range, when known.
          - `notes` (`string | null`) - Additional clinical notes about the observation.
          - `observation_code` (`string | null`) - The laboratory or clinical code that identifies the observation.
          - `observation_name` (`string | null`) - The observation's display name.
          - `observation_text` (`string | null`) - The textual observation value, when the result is not represented numerically.
          - `observation_value` (`number | null`) - The numeric value of the observation, when applicable.
          - `observation_value_precision` (`string | null`) - enum: `<`, `=`, `>`, `null`; The qualifier that indicates whether `observation_value` is exact or a boundary.
          - `observation_value_units` (`string | null`) - The unit used for `observation_value`.
          - `observed_at` (`string | null`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
          - `reference_range_lower_bound` (`number | null`) - The lower bound of the expected reference range, when supplied.
          - `reference_range_upper_bound` (`number | null`) - The upper bound of the expected reference range, when supplied.
          - `status` (`string | null`) - enum: `corrected`, `final`, `pending`, `null`; The clinical workflow status to assign to the observation.
      - `title` (`string`) - The display title of the test result.

### Example

```json
{
  "items": [
    {
      "automatically_create_resource_permission_for_patient": true,
      "notify_patient_of_resource_permission": true,
      "patient_id": "00000000-0000-4000-8000-000000000004",
      "status": "complete",
      "test_result_items": [
        {
          "observation_name": "Haemoglobin",
          "observation_value": 14.5,
          "observation_value_units": "g/dL",
          "status": "final"
        }
      ],
      "title": "Full blood count"
    }
  ]
}
```

## Response `201`

Every TestResult was created immediately.

- `object` - The response from `POST /v1/test_result_batches`. `remote_file_import_batch` is null when every TestResult was created immediately (`201`). It contains the import batch when at least one item includes `file_url` or `file_base64` (`202`).
  - `items` (`array`) - The per-item outcomes in their original request order.
    - `items` (`object`) - The result of one item in a Test Result batch. `test_result` contains the created TestResult when processing finishes immediately. For an imported file, it is null and `remote_file_import_batch_item_id` identifies the item being processed.
      - `object` (`any`) - Always `test_result_batch_item`.
      - `position` (`integer`) - The zero-based position of the item in the request `items` array.
      - `remote_file_import_batch_item_id` (`string | null`) - format: `uuid`; The import item identifier for a file that is still being processed. Null when the TestResult was created immediately. Poll the parent `remote_file_import_batch` for status.
      - `status` (`string`) - enum: `failed`, `pending`, `processing`, `succeeded`; The item's processing status. `succeeded` when the TestResult was created immediately; otherwise the current file import status.
      - `test_result` (`any`) - The created TestResult, or null while an imported file is being processed.
  - `object` (`any`) - Always `test_result_batch`.
  - `remote_file_import_batch` (`any`) - The file import batch to poll, or null when every TestResult was created immediately.

### Example

```json
{
  "object": "test_result_batch",
  "items": [
    {
      "object": "test_result_batch_item",
      "position": 0,
      "remote_file_import_batch_item_id": "57bed2bf-e2e1-463b-8a96-643e3817a8b8",
      "status": "failed",
      "test_result": {
        "id": "9d4a13e1-0eea-4669-88c4-03316c092c77",
        "object": "test_result",
        "automatically_create_resource_permission_for_patient": true,
        "created_at": "2026-01-01T09:00:00Z",
        "download_url": "https://files.example.invalid/document.pdf?signature=test",
        "filename": "referral-letter.pdf",
        "links": {
          "booking": "https://api.carebit.co/v1/bookings/92a3b4c5-d6e7-4f01-8234-56789abcdef0",
          "clinician": "https://api.carebit.co/v1/clinicians/2b3c4d5e-6f70-489a-9bcd-ef0123456789",
          "patient": "https://api.carebit.co/v1/patients/1a2b3c4d-5e6f-4789-8abc-def012345678",
          "remote_file_import_batch": "https://api.carebit.co/v1/remote_file_import_batches/ebc38802-f219-4c7b-8136-8e963a0c69e0"
        },
        "notify_patient_of_resource_permission": true,
        "remote_file_import_batch_id": "ebc38802-f219-4c7b-8136-8e963a0c69e0",
        "status": "awaiting_proofreading",
        "test_result_items": [
          {
            "id": "7592f451-4733-44f1-8560-1bb6075fa552",
            "object": "test_result_item",
            "created_at": "2026-01-01T09:00:00Z",
            "is_abnormal": false,
            "notes": "Please confirm the appointment by email.",
            "observation_code": "718-7",
            "observation_name": "Haemoglobin",
            "observation_text": "Within the expected range",
            "observation_value": 14.5,
            "observation_value_precision": "<",
            "observation_value_units": "g/dL",
            "observed_at": "2026-01-01T09:00:00Z",
            "reference_range_lower_bound": 12,
            "reference_range_upper_bound": 16,
            "status": "corrected",
            "updated_at": "2026-01-01T09:00:00Z"
          }
        ],
        "title": "Dr",
        "updated_at": "2026-01-01T09:00:00Z"
      }
    }
  ],
  "remote_file_import_batch": {
    "id": "ebc38802-f219-4c7b-8136-8e963a0c69e0",
    "object": "remote_file_import_batch",
    "completed_count": 0,
    "created_at": "2026-01-01T09:00:00Z",
    "failed_count": 0,
    "items": [
      {
        "id": "57bed2bf-e2e1-463b-8a96-643e3817a8b8",
        "object": "remote_file_import_batch_item",
        "created_resource": "https://api.carebit.co/v1/letters/c3d4e5f6-0718-49ab-acde-f01234567890",
        "created_resource_id": "7e09a8e3-e3c1-4dee-863b-85d56e4329da",
        "created_resource_type": null,
        "error": null,
        "position": 0,
        "status": "failed"
      }
    ],
    "links": {
      "self": "https://api.carebit.co/v1/remote_file_import_batches/ebc38802-f219-4c7b-8136-8e963a0c69e0"
    },
    "resource_type": "letter",
    "status": "completed",
    "total_count": 1,
    "updated_at": "2026-01-01T09:00:00Z"
  }
}
```

## Response `202`

At least one file is being imported. Poll `remote_file_import_batch` for each item's status.

- `object` - The response from `POST /v1/test_result_batches`. `remote_file_import_batch` is null when every TestResult was created immediately (`201`). It contains the import batch when at least one item includes `file_url` or `file_base64` (`202`).
  - `items` (`array`) - The per-item outcomes in their original request order.
    - `items` (`object`) - The result of one item in a Test Result batch. `test_result` contains the created TestResult when processing finishes immediately. For an imported file, it is null and `remote_file_import_batch_item_id` identifies the item being processed.
      - `object` (`any`) - Always `test_result_batch_item`.
      - `position` (`integer`) - The zero-based position of the item in the request `items` array.
      - `remote_file_import_batch_item_id` (`string | null`) - format: `uuid`; The import item identifier for a file that is still being processed. Null when the TestResult was created immediately. Poll the parent `remote_file_import_batch` for status.
      - `status` (`string`) - enum: `failed`, `pending`, `processing`, `succeeded`; The item's processing status. `succeeded` when the TestResult was created immediately; otherwise the current file import status.
      - `test_result` (`any`) - The created TestResult, or null while an imported file is being processed.
  - `object` (`any`) - Always `test_result_batch`.
  - `remote_file_import_batch` (`any`) - The file import batch to poll, or null when every TestResult was created immediately.

### Example

```json
{
  "object": "test_result_batch",
  "items": [
    {
      "object": "test_result_batch_item",
      "position": 0,
      "remote_file_import_batch_item_id": "57bed2bf-e2e1-463b-8a96-643e3817a8b8",
      "status": "failed",
      "test_result": {
        "id": "9d4a13e1-0eea-4669-88c4-03316c092c77",
        "object": "test_result",
        "automatically_create_resource_permission_for_patient": true,
        "created_at": "2026-01-01T09:00:00Z",
        "download_url": "https://files.example.invalid/document.pdf?signature=test",
        "filename": "referral-letter.pdf",
        "links": {
          "booking": "https://api.carebit.co/v1/bookings/92a3b4c5-d6e7-4f01-8234-56789abcdef0",
          "clinician": "https://api.carebit.co/v1/clinicians/2b3c4d5e-6f70-489a-9bcd-ef0123456789",
          "patient": "https://api.carebit.co/v1/patients/1a2b3c4d-5e6f-4789-8abc-def012345678",
          "remote_file_import_batch": "https://api.carebit.co/v1/remote_file_import_batches/ebc38802-f219-4c7b-8136-8e963a0c69e0"
        },
        "notify_patient_of_resource_permission": true,
        "remote_file_import_batch_id": "ebc38802-f219-4c7b-8136-8e963a0c69e0",
        "status": "awaiting_proofreading",
        "test_result_items": [
          {
            "id": "7592f451-4733-44f1-8560-1bb6075fa552",
            "object": "test_result_item",
            "created_at": "2026-01-01T09:00:00Z",
            "is_abnormal": false,
            "notes": "Please confirm the appointment by email.",
            "observation_code": "718-7",
            "observation_name": "Haemoglobin",
            "observation_text": "Within the expected range",
            "observation_value": 14.5,
            "observation_value_precision": "<",
            "observation_value_units": "g/dL",
            "observed_at": "2026-01-01T09:00:00Z",
            "reference_range_lower_bound": 12,
            "reference_range_upper_bound": 16,
            "status": "corrected",
            "updated_at": "2026-01-01T09:00:00Z"
          }
        ],
        "title": "Dr",
        "updated_at": "2026-01-01T09:00:00Z"
      }
    }
  ],
  "remote_file_import_batch": {
    "id": "ebc38802-f219-4c7b-8136-8e963a0c69e0",
    "object": "remote_file_import_batch",
    "completed_count": 0,
    "created_at": "2026-01-01T09:00:00Z",
    "failed_count": 0,
    "items": [
      {
        "id": "57bed2bf-e2e1-463b-8a96-643e3817a8b8",
        "object": "remote_file_import_batch_item",
        "created_resource": "https://api.carebit.co/v1/letters/c3d4e5f6-0718-49ab-acde-f01234567890",
        "created_resource_id": "7e09a8e3-e3c1-4dee-863b-85d56e4329da",
        "created_resource_type": null,
        "error": null,
        "position": 0,
        "status": "failed"
      }
    ],
    "links": {
      "self": "https://api.carebit.co/v1/remote_file_import_batches/ebc38802-f219-4c7b-8136-8e963a0c69e0"
    },
    "resource_type": "letter",
    "status": "completed",
    "total_count": 1,
    "updated_at": "2026-01-01T09:00:00Z"
  }
}
```

## Response `400`

The `Idempotency-Key` header is missing (`idempotency_key_required`) or exceeds 255 characters (`idempotency_key_too_long`).

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `401`

The access token is missing, invalid, expired, or revoked.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `403`

The access token lacks the required scope, or the project is disabled.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `404`

Error response.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `409`

A concurrent request holds the idempotency lease (`idempotency_conflict`). Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `422`

The `Idempotency-Key` was previously used with a different request body (`idempotency_key_reused`), or the request body failed validation.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `429`

Too many requests. Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Code samples

```bash
curl -X POST "https://api.carebit.co/v1/test_result_batches" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "automatically_create_resource_permission_for_patient": true,
      "notify_patient_of_resource_permission": true,
      "patient_id": "00000000-0000-4000-8000-000000000004",
      "status": "complete",
      "test_result_items": [
        {
          "observation_name": "Haemoglobin",
          "observation_value": 14.5,
          "observation_value_units": "g/dL",
          "status": "final"
        }
      ],
      "title": "Full blood count"
    }
  ]
}'
```

```javascript
const response = await fetch("https://api.carebit.co/v1/test_result_batches", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
  "items": [
    {
      "automatically_create_resource_permission_for_patient": true,
      "notify_patient_of_resource_permission": true,
      "patient_id": "00000000-0000-4000-8000-000000000004",
      "status": "complete",
      "test_result_items": [
        {
          "observation_name": "Haemoglobin",
          "observation_value": 14.5,
          "observation_value_units": "g/dL",
          "status": "final"
        }
      ],
      "title": "Full blood count"
    }
  ]
}),
});

if (!response.ok) {
  throw new Error(`Carebit API error: ${response.status}`);
}

const data = await response.json();
```

```python
import os
import requests
import uuid

response = requests.post(
    "https://api.carebit.co/v1/test_result_batches",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "items": [
            {
                "automatically_create_resource_permission_for_patient": True,
                "notify_patient_of_resource_permission": True,
                "patient_id": "00000000-0000-4000-8000-000000000004",
                "status": "complete",
                "test_result_items": [
                    {
                        "observation_name": "Haemoglobin",
                        "observation_value": 14.5,
                        "observation_value_units": "g/dL",
                        "status": "final"
                    }
                ],
                "title": "Full blood count"
            }
        ]
    }
)
response.raise_for_status()
data = response.json()
```

```ruby
require "httparty"
require "json"
require "securerandom"

response = HTTParty.post(
  "https://api.carebit.co/v1/test_result_batches",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}",
    "Idempotency-Key" => SecureRandom.uuid,
    "Content-Type" => "application/json"
  },
  body: {
    "items" => [
      {
        "automatically_create_resource_permission_for_patient" => true,
        "notify_patient_of_resource_permission" => true,
        "patient_id" => "00000000-0000-4000-8000-000000000004",
        "status" => "complete",
        "test_result_items" => [
          {
            "observation_name" => "Haemoglobin",
            "observation_value" => 14.5,
            "observation_value_units" => "g/dL",
            "status" => "final"
          }
        ],
        "title" => "Full blood count"
      }
    ]
  }.to_json
)
raise "Carebit API error: #{response.code}" unless response.success?
data = response.parsed_response
```

```php
<?php

$client = new GuzzleHttp\Client();

$response = $client->post("https://api.carebit.co/v1/test_result_batches", [
    "headers" => [
      "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
      "Idempotency-Key" => bin2hex(random_bytes(16)),
    ],
    "json" => [
      "items" => [
        [
          "automatically_create_resource_permission_for_patient" => true,
          "notify_patient_of_resource_permission" => true,
          "patient_id" => "00000000-0000-4000-8000-000000000004",
          "status" => "complete",
          "test_result_items" => [
            [
              "observation_name" => "Haemoglobin",
              "observation_value" => 14.5,
              "observation_value_units" => "g/dL",
              "status" => "final"
            ]
          ],
          "title" => "Full blood count"
        ]
      ]
    ]
]);
$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```


## List Test Results

`GET /v1/test_results`

**Required API scopes:** `test_results.read`

## Parameters

- `patient_id` (query, `string`)
- `booking_id` (query, `string`)
- `status` (query, `string`)
- `created_since` (query, `string`)
- `limit` (query, `integer`) - The maximum number of items to return. Defaults to `25`; the maximum is `100`.
- `starting_after` (query, `string`) - Return items after this resource ID. You cannot use this with `cursor`.
- `cursor` (query, `string`) - The `next_cursor` value from the previous page. You cannot use this with `starting_after`.

## Response `200`

Paginated list of `TestResult` objects.

- `any`

### Example

```json
{
  "object": "list",
  "data": [
    {
      "id": "9d4a13e1-0eea-4669-88c4-03316c092c77",
      "object": "test_result",
      "automatically_create_resource_permission_for_patient": true,
      "created_at": "2026-01-01T09:00:00Z",
      "download_url": "https://files.example.invalid/document.pdf?signature=test",
      "filename": "referral-letter.pdf",
      "links": {
        "booking": "https://api.carebit.co/v1/bookings/92a3b4c5-d6e7-4f01-8234-56789abcdef0",
        "clinician": "https://api.carebit.co/v1/clinicians/2b3c4d5e-6f70-489a-9bcd-ef0123456789",
        "patient": "https://api.carebit.co/v1/patients/1a2b3c4d-5e6f-4789-8abc-def012345678",
        "remote_file_import_batch": "https://api.carebit.co/v1/remote_file_import_batches/ebc38802-f219-4c7b-8136-8e963a0c69e0"
      },
      "notify_patient_of_resource_permission": true,
      "remote_file_import_batch_id": "ebc38802-f219-4c7b-8136-8e963a0c69e0",
      "status": "awaiting_proofreading",
      "test_result_items": [
        {
          "id": "7592f451-4733-44f1-8560-1bb6075fa552",
          "object": "test_result_item",
          "created_at": "2026-01-01T09:00:00Z",
          "is_abnormal": false,
          "notes": "Please confirm the appointment by email.",
          "observation_code": "718-7",
          "observation_name": "Haemoglobin",
          "observation_text": "Within the expected range",
          "observation_value": 14.5,
          "observation_value_precision": "<",
          "observation_value_units": "g/dL",
          "observed_at": "2026-01-01T09:00:00Z",
          "reference_range_lower_bound": 12,
          "reference_range_upper_bound": 16,
          "status": "corrected",
          "updated_at": "2026-01-01T09:00:00Z"
        }
      ],
      "title": "Dr",
      "updated_at": "2026-01-01T09:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": "eyJzdGFydF90aW1lIjoiMjAyNi0wMS0wMVQwOTowMDowMFoifQ",
  "url": "/v1/test_results"
}
```

## Response `400`

A filter or pagination parameter is invalid.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `401`

The access token is missing, invalid, expired, or revoked.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `403`

The access token lacks the required scope, or the project is disabled.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `429`

Too many requests. Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Code samples

```bash
curl -X GET "https://api.carebit.co/v1/test_results" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN"
```

```javascript
const response = await fetch("https://api.carebit.co/v1/test_results", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
  },
});

if (!response.ok) {
  throw new Error(`Carebit API error: ${response.status}`);
}

const data = await response.json();
```

```python
import os
import requests

response = requests.get(
    "https://api.carebit.co/v1/test_results",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
    }
)
response.raise_for_status()
data = response.json()
```

```ruby
require "httparty"

response = HTTParty.get(
  "https://api.carebit.co/v1/test_results",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}"
  }
)
raise "Carebit API error: #{response.code}" unless response.success?
data = response.parsed_response
```

```php
<?php

$client = new GuzzleHttp\Client();

$response = $client->get("https://api.carebit.co/v1/test_results", [
    "headers" => [
      "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
    ]
]);
$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```


## Create a Test Result

`POST /v1/test_results`

Returns the created TestResult with status `201`. If you provide `file_url` or `file_base64`, the response also identifies a remote file import batch. Poll that batch for the file's import status.

**Required API scopes:** `test_results.create`

## Parameters

- `Idempotency-Key` (header, `string`) (required) - Client-generated idempotency key. Required for every POST/PATCH write. Replay of the same key with the same body returns the stored response with an `Idempotency-Replayed: true` header. Same key + different body returns `422 idempotency_key_reused`. A duplicate that arrives while the first request is still in flight returns `409 idempotency_conflict` with `Retry-After: 1`.

## Request body (`application/json`)

- `object`
  - `automatically_create_resource_permission_for_patient` (`boolean`) - Whether Carebit should automatically share the test result with the patient after processing.
  - `booking_id` (`string | null`) - format: `uuid`; The identifier of the booking associated with the test result, or null when it is not linked to a booking.
  - `clinician_id` (`string | null`) - format: `uuid`; The identifier of the clinician associated with the test result, or null when none is assigned.
  - `description` (`string | null`) - A description of the TestResult.
  - `file_base64` (`string | null`) - format: `byte`; The test result bytes encoded as Base64. Provide this with `filename` instead of `file_url`. The decoded file can be at most 7 MB.
  - `file_url` (`string | null`) - format: `uri`; The public HTTPS URL of a test result document that Carebit should download.
  - `filename` (`string | null`) - The filename to use for the test result. Required with `file_base64`; defaults to the remote file's filename for URL sources.
  - `notify_patient_of_resource_permission` (`boolean`) - Whether Carebit should notify the patient when the test result is shared with them.
  - `patient_id` (`string`) - format: `uuid`; The identifier of the patient that the test result belongs to.
  - `status` (`string`) - enum: `awaiting_review`, `complete`, `draft`, `reviewed`; The workflow status to assign to the test result.
  - `test_result_items` (`array`) - The structured clinical observations to include in the test result.
    - `items` (`object`)
      - `is_abnormal` (`boolean | null`) - Whether the observation falls outside its reference range, when known.
      - `notes` (`string | null`) - Additional clinical notes about the observation.
      - `observation_code` (`string | null`) - The laboratory or clinical code that identifies the observation.
      - `observation_name` (`string | null`) - The observation's display name.
      - `observation_text` (`string | null`) - The textual observation value, when the result is not represented numerically.
      - `observation_value` (`number | null`) - The numeric value of the observation, when applicable.
      - `observation_value_precision` (`string | null`) - enum: `<`, `=`, `>`, `null`; The qualifier that indicates whether `observation_value` is exact or a boundary.
      - `observation_value_units` (`string | null`) - The unit used for `observation_value`.
      - `observed_at` (`string | null`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
      - `reference_range_lower_bound` (`number | null`) - The lower bound of the expected reference range, when supplied.
      - `reference_range_upper_bound` (`number | null`) - The upper bound of the expected reference range, when supplied.
      - `status` (`string | null`) - enum: `corrected`, `final`, `pending`, `null`; The clinical workflow status to assign to the observation.
  - `title` (`string`) - The display title of the test result.

### Example

```json
{
  "automatically_create_resource_permission_for_patient": true,
  "notify_patient_of_resource_permission": true,
  "patient_id": "00000000-0000-4000-8000-000000000004",
  "status": "complete",
  "test_result_items": [
    {
      "is_abnormal": false,
      "observation_code": "718-7",
      "observation_name": "Haemoglobin",
      "observation_value": 14.5,
      "observation_value_units": "g/dL",
      "reference_range_lower_bound": 12,
      "reference_range_upper_bound": 16,
      "status": "final"
    }
  ],
  "title": "Full blood count"
}
```

## Response `201`

The requested `TestResult`.

- `object`
  - `automatically_create_resource_permission_for_patient` (`boolean`) - Whether Carebit automatically shares the test result with the patient after processing.
  - `created_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
  - `download_url` (`string | null`) - format: `uri`; The short-lived signed download URL for the TestResult. Null until the uploaded file passes malware scanning.
  - `filename` (`string | null`) - The original filename of the test result document, when one was supplied.
  - `id` (`string`) - format: `uuid`; The resource's unique identifier, formatted as an RFC 4122 version 4 UUID.
  - `links` (`object`) - URLs to related resources.
    - `booking` (`string | null`) - format: `uri`; The full URL of a related resource.
    - `clinician` (`string | null`) - format: `uri`; The full URL of a related resource.
    - `patient` (`string | null`) - format: `uri`; The full URL of a related resource.
    - `remote_file_import_batch` (`string`) - format: `uri`; The full URL of a related resource.
  - `notify_patient_of_resource_permission` (`boolean | null`) - Whether Carebit notifies the patient when the test result is shared with them.
  - `object` (`any`) - Discriminator value emitted at `object`.
  - `remote_file_import_batch_id` (`string`) - format: `uuid`; The identifier of the remote file import batch created for the uploaded file. Set on the create response when `file_url` or `file_base64` was submitted.
  - `status` (`string | null`) - enum: `awaiting_proofreading`, `awaiting_receipt`, `awaiting_review`, `awaiting_sending`, `awaiting_typing`, `complete`, `draft`, `reviewed`, `null`; The workflow status of the test result.
  - `test_result_items` (`array`) - The structured clinical observations included in the test result.
    - `items` (`object`)
      - `created_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
      - `id` (`string`) - format: `uuid`; The resource's unique identifier, formatted as an RFC 4122 version 4 UUID.
      - `is_abnormal` (`boolean | null`) - Whether the observation falls outside its reference range, when known.
      - `notes` (`string | null`) - Additional clinical notes about the observation.
      - `object` (`any`) - Discriminator value emitted at `object`.
      - `observation_code` (`string | null`) - The laboratory or clinical code that identifies the observation.
      - `observation_name` (`string | null`) - The observation's display name.
      - `observation_text` (`string | null`) - The textual observation value, when the result is not represented numerically.
      - `observation_value` (`number | null`) - The numeric value of the observation, when applicable.
      - `observation_value_precision` (`string | null`) - enum: `<`, `=`, `>`, `null`; The precision qualifier for `observation_value`. `<` and `>` denote a bound, and `=` denotes an exact value.
      - `observation_value_units` (`string | null`) - The unit used for `observation_value`.
      - `observed_at` (`string | null`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
      - `reference_range_lower_bound` (`number | null`) - The lower bound of the expected reference range, when supplied.
      - `reference_range_upper_bound` (`number | null`) - The upper bound of the expected reference range, when supplied.
      - `status` (`string | null`) - enum: `corrected`, `final`, `pending`, `null`; The clinical workflow status of the observation.
      - `updated_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
  - `title` (`string | null`) - The display title of the test result.
  - `updated_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.

### Example

```json
{
  "id": "9d4a13e1-0eea-4669-88c4-03316c092c77",
  "object": "test_result",
  "automatically_create_resource_permission_for_patient": true,
  "created_at": "2026-01-01T09:00:00Z",
  "download_url": "https://files.example.invalid/document.pdf?signature=test",
  "filename": "referral-letter.pdf",
  "links": {
    "booking": "https://api.carebit.co/v1/bookings/92a3b4c5-d6e7-4f01-8234-56789abcdef0",
    "clinician": "https://api.carebit.co/v1/clinicians/2b3c4d5e-6f70-489a-9bcd-ef0123456789",
    "patient": "https://api.carebit.co/v1/patients/1a2b3c4d-5e6f-4789-8abc-def012345678",
    "remote_file_import_batch": "https://api.carebit.co/v1/remote_file_import_batches/ebc38802-f219-4c7b-8136-8e963a0c69e0"
  },
  "notify_patient_of_resource_permission": true,
  "remote_file_import_batch_id": "ebc38802-f219-4c7b-8136-8e963a0c69e0",
  "status": "awaiting_proofreading",
  "test_result_items": [
    {
      "id": "7592f451-4733-44f1-8560-1bb6075fa552",
      "object": "test_result_item",
      "created_at": "2026-01-01T09:00:00Z",
      "is_abnormal": false,
      "notes": "Please confirm the appointment by email.",
      "observation_code": "718-7",
      "observation_name": "Haemoglobin",
      "observation_text": "Within the expected range",
      "observation_value": 14.5,
      "observation_value_precision": "<",
      "observation_value_units": "g/dL",
      "observed_at": "2026-01-01T09:00:00Z",
      "reference_range_lower_bound": 12,
      "reference_range_upper_bound": 16,
      "status": "corrected",
      "updated_at": "2026-01-01T09:00:00Z"
    }
  ],
  "title": "Dr",
  "updated_at": "2026-01-01T09:00:00Z"
}
```

## Response `400`

The request contains more test result items than the per-request limit permits.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `401`

The access token is missing, invalid, expired, or revoked.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `403`

The access token lacks the required scope, or the project is disabled.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `404`

Error response.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `409`

A concurrent request holds the idempotency lease (`idempotency_conflict`). Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `422`

The `Idempotency-Key` was previously used with a different request body (`idempotency_key_reused`), or the request body failed validation.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `429`

Too many requests. Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Code samples

```bash
curl -X POST "https://api.carebit.co/v1/test_results" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "automatically_create_resource_permission_for_patient": true,
  "notify_patient_of_resource_permission": true,
  "patient_id": "00000000-0000-4000-8000-000000000004",
  "status": "complete",
  "test_result_items": [
    {
      "is_abnormal": false,
      "observation_code": "718-7",
      "observation_name": "Haemoglobin",
      "observation_value": 14.5,
      "observation_value_units": "g/dL",
      "reference_range_lower_bound": 12,
      "reference_range_upper_bound": 16,
      "status": "final"
    }
  ],
  "title": "Full blood count"
}'
```

```javascript
const response = await fetch("https://api.carebit.co/v1/test_results", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
  "automatically_create_resource_permission_for_patient": true,
  "notify_patient_of_resource_permission": true,
  "patient_id": "00000000-0000-4000-8000-000000000004",
  "status": "complete",
  "test_result_items": [
    {
      "is_abnormal": false,
      "observation_code": "718-7",
      "observation_name": "Haemoglobin",
      "observation_value": 14.5,
      "observation_value_units": "g/dL",
      "reference_range_lower_bound": 12,
      "reference_range_upper_bound": 16,
      "status": "final"
    }
  ],
  "title": "Full blood count"
}),
});

if (!response.ok) {
  throw new Error(`Carebit API error: ${response.status}`);
}

const data = await response.json();
```

```python
import os
import requests
import uuid

response = requests.post(
    "https://api.carebit.co/v1/test_results",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "automatically_create_resource_permission_for_patient": True,
        "notify_patient_of_resource_permission": True,
        "patient_id": "00000000-0000-4000-8000-000000000004",
        "status": "complete",
        "test_result_items": [
            {
                "is_abnormal": False,
                "observation_code": "718-7",
                "observation_name": "Haemoglobin",
                "observation_value": 14.5,
                "observation_value_units": "g/dL",
                "reference_range_lower_bound": 12,
                "reference_range_upper_bound": 16,
                "status": "final"
            }
        ],
        "title": "Full blood count"
    }
)
response.raise_for_status()
data = response.json()
```

```ruby
require "httparty"
require "json"
require "securerandom"

response = HTTParty.post(
  "https://api.carebit.co/v1/test_results",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}",
    "Idempotency-Key" => SecureRandom.uuid,
    "Content-Type" => "application/json"
  },
  body: {
    "automatically_create_resource_permission_for_patient" => true,
    "notify_patient_of_resource_permission" => true,
    "patient_id" => "00000000-0000-4000-8000-000000000004",
    "status" => "complete",
    "test_result_items" => [
      {
        "is_abnormal" => false,
        "observation_code" => "718-7",
        "observation_name" => "Haemoglobin",
        "observation_value" => 14.5,
        "observation_value_units" => "g/dL",
        "reference_range_lower_bound" => 12,
        "reference_range_upper_bound" => 16,
        "status" => "final"
      }
    ],
    "title" => "Full blood count"
  }.to_json
)
raise "Carebit API error: #{response.code}" unless response.success?
data = response.parsed_response
```

```php
<?php

$client = new GuzzleHttp\Client();

$response = $client->post("https://api.carebit.co/v1/test_results", [
    "headers" => [
      "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
      "Idempotency-Key" => bin2hex(random_bytes(16)),
    ],
    "json" => [
      "automatically_create_resource_permission_for_patient" => true,
      "notify_patient_of_resource_permission" => true,
      "patient_id" => "00000000-0000-4000-8000-000000000004",
      "status" => "complete",
      "test_result_items" => [
        [
          "is_abnormal" => false,
          "observation_code" => "718-7",
          "observation_name" => "Haemoglobin",
          "observation_value" => 14.5,
          "observation_value_units" => "g/dL",
          "reference_range_lower_bound" => 12,
          "reference_range_upper_bound" => 16,
          "status" => "final"
        ]
      ],
      "title" => "Full blood count"
    ]
]);
$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```


## Get a Test Result

`GET /v1/test_results/:id`

**Required API scopes:** `test_results.read`

## Parameters

- `id` (path, `string`) (required)

## Response `200`

The requested `TestResult`.

- `object`
  - `automatically_create_resource_permission_for_patient` (`boolean`) - Whether Carebit automatically shares the test result with the patient after processing.
  - `created_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
  - `download_url` (`string | null`) - format: `uri`; The short-lived signed download URL for the TestResult. Null until the uploaded file passes malware scanning.
  - `filename` (`string | null`) - The original filename of the test result document, when one was supplied.
  - `id` (`string`) - format: `uuid`; The resource's unique identifier, formatted as an RFC 4122 version 4 UUID.
  - `links` (`object`) - URLs to related resources.
    - `booking` (`string | null`) - format: `uri`; The full URL of a related resource.
    - `clinician` (`string | null`) - format: `uri`; The full URL of a related resource.
    - `patient` (`string | null`) - format: `uri`; The full URL of a related resource.
    - `remote_file_import_batch` (`string`) - format: `uri`; The full URL of a related resource.
  - `notify_patient_of_resource_permission` (`boolean | null`) - Whether Carebit notifies the patient when the test result is shared with them.
  - `object` (`any`) - Discriminator value emitted at `object`.
  - `remote_file_import_batch_id` (`string`) - format: `uuid`; The identifier of the remote file import batch created for the uploaded file. Set on the create response when `file_url` or `file_base64` was submitted.
  - `status` (`string | null`) - enum: `awaiting_proofreading`, `awaiting_receipt`, `awaiting_review`, `awaiting_sending`, `awaiting_typing`, `complete`, `draft`, `reviewed`, `null`; The workflow status of the test result.
  - `test_result_items` (`array`) - The structured clinical observations included in the test result.
    - `items` (`object`)
      - `created_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
      - `id` (`string`) - format: `uuid`; The resource's unique identifier, formatted as an RFC 4122 version 4 UUID.
      - `is_abnormal` (`boolean | null`) - Whether the observation falls outside its reference range, when known.
      - `notes` (`string | null`) - Additional clinical notes about the observation.
      - `object` (`any`) - Discriminator value emitted at `object`.
      - `observation_code` (`string | null`) - The laboratory or clinical code that identifies the observation.
      - `observation_name` (`string | null`) - The observation's display name.
      - `observation_text` (`string | null`) - The textual observation value, when the result is not represented numerically.
      - `observation_value` (`number | null`) - The numeric value of the observation, when applicable.
      - `observation_value_precision` (`string | null`) - enum: `<`, `=`, `>`, `null`; The precision qualifier for `observation_value`. `<` and `>` denote a bound, and `=` denotes an exact value.
      - `observation_value_units` (`string | null`) - The unit used for `observation_value`.
      - `observed_at` (`string | null`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
      - `reference_range_lower_bound` (`number | null`) - The lower bound of the expected reference range, when supplied.
      - `reference_range_upper_bound` (`number | null`) - The upper bound of the expected reference range, when supplied.
      - `status` (`string | null`) - enum: `corrected`, `final`, `pending`, `null`; The clinical workflow status of the observation.
      - `updated_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.
  - `title` (`string | null`) - The display title of the test result.
  - `updated_at` (`string`) - format: `date-time`; An ISO 8601 timestamp in UTC, with a `Z` suffix. For example, `2026-07-01T09:00:00Z`.

### Example

```json
{
  "id": "9d4a13e1-0eea-4669-88c4-03316c092c77",
  "object": "test_result",
  "automatically_create_resource_permission_for_patient": true,
  "created_at": "2026-01-01T09:00:00Z",
  "download_url": "https://files.example.invalid/document.pdf?signature=test",
  "filename": "referral-letter.pdf",
  "links": {
    "booking": "https://api.carebit.co/v1/bookings/92a3b4c5-d6e7-4f01-8234-56789abcdef0",
    "clinician": "https://api.carebit.co/v1/clinicians/2b3c4d5e-6f70-489a-9bcd-ef0123456789",
    "patient": "https://api.carebit.co/v1/patients/1a2b3c4d-5e6f-4789-8abc-def012345678",
    "remote_file_import_batch": "https://api.carebit.co/v1/remote_file_import_batches/ebc38802-f219-4c7b-8136-8e963a0c69e0"
  },
  "notify_patient_of_resource_permission": true,
  "remote_file_import_batch_id": "ebc38802-f219-4c7b-8136-8e963a0c69e0",
  "status": "awaiting_proofreading",
  "test_result_items": [
    {
      "id": "7592f451-4733-44f1-8560-1bb6075fa552",
      "object": "test_result_item",
      "created_at": "2026-01-01T09:00:00Z",
      "is_abnormal": false,
      "notes": "Please confirm the appointment by email.",
      "observation_code": "718-7",
      "observation_name": "Haemoglobin",
      "observation_text": "Within the expected range",
      "observation_value": 14.5,
      "observation_value_precision": "<",
      "observation_value_units": "g/dL",
      "observed_at": "2026-01-01T09:00:00Z",
      "reference_range_lower_bound": 12,
      "reference_range_upper_bound": 16,
      "status": "corrected",
      "updated_at": "2026-01-01T09:00:00Z"
    }
  ],
  "title": "Dr",
  "updated_at": "2026-01-01T09:00:00Z"
}
```

## Response `401`

The access token is missing, invalid, expired, or revoked.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `403`

The access token lacks the required scope, or the project is disabled.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `404`

Error response.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Response `429`

Too many requests. Retry after the delay indicated by `Retry-After`.

- `object`
  - `error` (`object`) - The structured details that describe why the request failed.
    - `code` (`string`) - The machine-readable error code.
    - `errors` (`array | null`) - Additional errors from a failed validation.
      - `items` (`object`)
        - `code` (`string`) - The machine-readable code for this validation error.
        - `message` (`string`) - A message that explains this validation error.
        - `param` (`string | null`) - The name of the parameter that caused this validation error, when known.
    - `message` (`string`) - A message that explains the error and how to resolve it.
    - `param` (`string | null`) - The name of the parameter that caused the error, when known.
    - `type` (`string`) - enum: `authentication_error`, `permission_error`, `invalid_request_error`, `rate_limit_error`, `api_error`; The high-level category of the error.

### Example

```json
{
  "error": {
    "code": "resource_missing",
    "errors": [
      {
        "code": "resource_missing",
        "message": "The requested resource was not found.",
        "param": "patient_id"
      }
    ],
    "message": "The requested resource was not found.",
    "param": "patient_id",
    "type": "authentication_error"
  }
}
```

## Code samples

```bash
curl -X GET "https://api.carebit.co/v1/test_results/8f14e45f-ea7d-4b6f-9c2a-1d3e5f7a9b0c" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN"
```

```javascript
const response = await fetch("https://api.carebit.co/v1/test_results/8f14e45f-ea7d-4b6f-9c2a-1d3e5f7a9b0c", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
  },
});

if (!response.ok) {
  throw new Error(`Carebit API error: ${response.status}`);
}

const data = await response.json();
```

```python
import os
import requests

response = requests.get(
    "https://api.carebit.co/v1/test_results/8f14e45f-ea7d-4b6f-9c2a-1d3e5f7a9b0c",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
    }
)
response.raise_for_status()
data = response.json()
```

```ruby
require "httparty"

response = HTTParty.get(
  "https://api.carebit.co/v1/test_results/8f14e45f-ea7d-4b6f-9c2a-1d3e5f7a9b0c",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}"
  }
)
raise "Carebit API error: #{response.code}" unless response.success?
data = response.parsed_response
```

```php
<?php

$client = new GuzzleHttp\Client();

$response = $client->get("https://api.carebit.co/v1/test_results/8f14e45f-ea7d-4b6f-9c2a-1d3e5f7a9b0c", [
    "headers" => [
      "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
    ]
]);
$data = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```

