> 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/patients-and-catalog/patients.md).

# Patients API

Create, list, update, and delete patients for your organization

{% hint style="info" %}
The Patients API lets you manage patients programmatically — create records, import in bulk, and link them to blood test results via `patient_id`.
{% endhint %}

## Overview

Patients are scoped to your organization. Each patient has a unique `email` and `patient_id` within the organization. The `patient_id` links patients to blood tests submitted via `/process` or `/upload` (the `patient_id` field).

All endpoints require `Authorization: Bearer YOUR_API_KEY`.

***

## Create Patient

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

### Request

```json
{
  "email": "patient@example.com",
  "patient_id": "PAT-001",
  "name": "John Doe",
  "phone": "+1234567890",
  "birth_date": "1990-05-15",
  "gender": "male"
}
```

| Field        | Type   | Required | Description                                                |
| ------------ | ------ | -------- | ---------------------------------------------------------- |
| `email`      | string | Yes      | Patient email (unique per organization)                    |
| `patient_id` | string | Yes      | Your identifier for this patient (unique per organization) |
| `name`       | string | No       | Patient full name                                          |
| `phone`      | string | No       | Phone number                                               |
| `birth_date` | string | No       | Date of birth (ISO 8601)                                   |
| `gender`     | string | No       | `male`, `female`, or `other`                               |

### cURL Example

```bash
curl -X POST https://sandbox.bloodgpt.com/api/v1/patients \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "patient@example.com",
    "patient_id": "PAT-001",
    "name": "John Doe",
    "birth_date": "1990-05-15",
    "gender": "male"
  }'
```

### Response (201 Created)

```json
{
  "request_id": "req_abc123",
  "patient": {
    "id": "clxyz789012",
    "email": "patient@example.com",
    "patient_id": "PAT-001",
    "name": "John Doe",
    "phone": null,
    "birth_date": "1990-05-15T00:00:00.000Z",
    "gender": "male",
    "created_at": "2025-02-10T12:00:00.000Z",
    "updated_at": "2025-02-10T12:00:00.000Z",
    "order_count": 0,
    "test_count": 0
  }
}
```

### Error Responses

<details>

<summary>400 Bad Request</summary>

```json
{
  "error": "invalid_email",
  "message": "Email is required"
}
```

```json
{
  "error": "missing_patient_id",
  "message": "External Patient ID is required"
}
```

</details>

<details>

<summary>409 Conflict</summary>

```json
{
  "error": "duplicate_email",
  "message": "A patient with this email already exists"
}
```

```json
{
  "error": "duplicate_patient_id",
  "message": "A patient with this Patient ID already exists"
}
```

</details>

***

## List Patients

**Endpoint:** `GET /api/v1/patients`

### Query Parameters

| Parameter | Type    | Default | Description                                     |
| --------- | ------- | ------- | ----------------------------------------------- |
| `limit`   | integer | 100     | Max patients to return (max 1000)               |
| `offset`  | integer | 0       | Number of patients to skip                      |
| `search`  | string  | —       | Search by name, email, or external\_patient\_id |

### cURL Example

```bash
curl "https://sandbox.bloodgpt.com/api/v1/patients?limit=50&search=john" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Response (200 OK)

```json
{
  "request_id": "req_abc123",
  "patients": [
    {
      "id": "clxyz789012",
      "email": "john@example.com",
      "patient_id": "PAT-001",
      "name": "John Doe",
      "phone": null,
      "birth_date": "1990-05-15T00:00:00.000Z",
      "gender": "male",
      "created_at": "2025-02-10T12:00:00.000Z",
      "updated_at": "2025-02-10T12:00:00.000Z",
      "order_count": 2,
      "test_count": 5
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}
```

***

## Get Patient

**Endpoint:** `GET /api/v1/patients/{patientId}`

### cURL Example

```bash
curl https://sandbox.bloodgpt.com/api/v1/patients/clxyz789012 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Response (200 OK)

```json
{
  "request_id": "req_abc123",
  "patient": {
    "id": "clxyz789012",
    "email": "john@example.com",
    "patient_id": "PAT-001",
    "name": "John Doe",
    "phone": null,
    "birth_date": "1990-05-15T00:00:00.000Z",
    "gender": "male",
    "created_at": "2025-02-10T12:00:00.000Z",
    "updated_at": "2025-02-10T12:00:00.000Z",
    "order_count": 2,
    "test_count": 5
  }
}
```

<details>

<summary>404 Not Found</summary>

```json
{
  "error": "patient_not_found",
  "message": "Patient not found"
}
```

</details>

***

## Update Patient

**Endpoint:** `PATCH /api/v1/patients/{patientId}`

Send only the fields you want to update.

### Request

```json
{
  "name": "Jane Doe",
  "phone": "+9876543210"
}
```

All fields from Create are supported, all optional.

### cURL Example

```bash
curl -X PATCH https://sandbox.bloodgpt.com/api/v1/patients/clxyz789012 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Jane Doe", "phone": "+9876543210"}'
```

### Response (200 OK)

```json
{
  "request_id": "req_abc123",
  "patient": {
    "id": "clxyz789012",
    "email": "john@example.com",
    "patient_id": "PAT-001",
    "name": "Jane Doe",
    "phone": "+9876543210",
    "birth_date": "1990-05-15T00:00:00.000Z",
    "gender": "male",
    "created_at": "2025-02-10T12:00:00.000Z",
    "updated_at": "2025-02-10T14:30:00.000Z",
    "order_count": 2,
    "test_count": 5
  }
}
```

***

## Delete Patient

**Endpoint:** `DELETE /api/v1/patients/{patientId}`

{% hint style="warning" %}
Patients with existing orders cannot be deleted. Cancel orders first.
{% endhint %}

### cURL Example

```bash
curl -X DELETE https://sandbox.bloodgpt.com/api/v1/patients/clxyz789012 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Response (200 OK)

```json
{
  "request_id": "req_abc123",
  "deleted": true
}
```

<details>

<summary>400 Bad Request — has orders</summary>

```json
{
  "error": "has_orders",
  "message": "Cannot delete patient with 3 order(s). Please cancel orders first."
}
```

</details>

***

## Bulk Import

**Endpoint:** `POST /api/v1/patients/import`

Import multiple patients in a single request.

### Request

```json
{
  "patients": [
    {
      "email": "alice@example.com",
      "patient_id": "PAT-002",
      "name": "Alice Smith",
      "gender": "female"
    },
    {
      "email": "bob@example.com",
      "patient_id": "PAT-003",
      "name": "Bob Johnson",
      "birth_date": "1985-11-20",
      "gender": "male"
    }
  ]
}
```

### Behavior

| Condition                 | Result                             |
| ------------------------- | ---------------------------------- |
| Email already exists      | **Skipped** (not counted as error) |
| Patient ID already exists | **Error**                          |
| Missing required fields   | **Error**                          |
| Invalid email format      | **Error**                          |

### cURL Example

```bash
curl -X POST https://sandbox.bloodgpt.com/api/v1/patients/import \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "patients": [
      {"email": "alice@example.com", "patient_id": "PAT-002", "name": "Alice"},
      {"email": "bob@example.com", "patient_id": "PAT-003", "name": "Bob"}
    ]
  }'
```

### Response (200 OK)

```json
{
  "request_id": "req_abc123",
  "imported": 2,
  "skipped": 0,
  "errors": [],
  "totalErrors": 0
}
```

### Response with errors

```json
{
  "request_id": "req_abc123",
  "imported": 1,
  "skipped": 1,
  "errors": [
    {
      "row": 3,
      "email": "bad-email",
      "reason": "Invalid email format"
    }
  ],
  "totalErrors": 1
}
```

{% hint style="info" %}
The `errors` array is capped at 10 items. Check `totalErrors` for the full count.
{% endhint %}

***

## Patient Fields Reference

| Field         | Type           | Description                                   |
| ------------- | -------------- | --------------------------------------------- |
| `id`          | string         | Internal patient ID (CUID)                    |
| `email`       | string         | Patient email (lowercased, unique per org)    |
| `patient_id`  | string         | Your patient identifier (unique per org)      |
| `name`        | string \| null | Full name                                     |
| `phone`       | string \| null | Phone number                                  |
| `birth_date`  | string \| null | Date of birth (ISO 8601)                      |
| `gender`      | string \| null | `male`, `female`, `other`                     |
| `created_at`  | string         | Creation timestamp                            |
| `updated_at`  | string         | Last update timestamp                         |
| `order_count` | integer        | Number of test orders                         |
| `test_count`  | integer        | Number of blood tests linked via `patient_id` |
