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

# Patient API

> Search and retrieve patient records via the OpenEyes API

## Overview

The Patient API provides endpoints for searching patient records in the OpenEyes system. This API is used for patient lookup, integration with external systems, and patient data retrieval.

Source: `protected/modules/Api/controllers/PatientController.php`

## Authentication

All Patient API endpoints require:

* HTTP Basic Authentication
* User must have the `OprnApi` role
* Authenticated user access (`users => ['@']`)

See [Authentication](/api/authentication) for details.

## Endpoints

### Search Patients

Search for patients by term and optional identifier type.

<CodeGroup>
  ```http Request theme={null}
  GET /api/v1/patient/search?term={term}&patient_identifier_type={type}
  ```

  ```bash cURL theme={null}
  curl -X GET \
    "https://your-instance.com/api/v1/patient/search?term=Smith" \
    -u username:password \
    -H "Content-Type: application/json"
  ```

  ```javascript JavaScript theme={null}
  const credentials = btoa('username:password');

  fetch('https://your-instance.com/api/v1/patient/search?term=Smith', {
    method: 'GET',
    headers: {
      'Authorization': `Basic ${credentials}`,
      'Content-Type': 'application/json'
    }
  })
  .then(response => response.json())
  .then(data => console.log(data));
  ```

  ```python Python theme={null}
  import requests
  from requests.auth import HTTPBasicAuth

  response = requests.get(
      'https://your-instance.com/api/v1/patient/search',
      params={'term': 'Smith'},
      auth=HTTPBasicAuth('username', 'password')
  )

  patients = response.json()
  ```
</CodeGroup>

#### Query Parameters

<ParamField query="term" type="string" required>
  Search term for patient lookup. Can be a name, identifier, or partial match.

  The term is validated using `PatientSearch::getValidSearchTerm()` before processing.
</ParamField>

<ParamField query="patient_identifier_type" type="integer" optional>
  Filter by specific patient identifier type ID.

  When provided, searches only within that identifier type. When omitted, searches across all identifier types and uses the primary identifier for display.
</ParamField>

#### Response

Returns an array of patient objects matching the search criteria.

<ResponseField name="id" type="integer">
  Patient database ID
</ResponseField>

<ResponseField name="first_name" type="string">
  Patient's first name
</ResponseField>

<ResponseField name="last_name" type="string">
  Patient's last name
</ResponseField>

<ResponseField name="age" type="string">
  Patient age or "Deceased" if patient has died
</ResponseField>

<ResponseField name="genderletter" type="string">
  Patient gender code (e.g., "M", "F")
</ResponseField>

<ResponseField name="dob" type="string">
  Date of birth in YYYY-MM-DD format
</ResponseField>

<ResponseField name="is_deceased" type="boolean">
  Whether the patient is deceased
</ResponseField>

<ResponseField name="patient_identifiers" type="array">
  Array of all patient identifiers

  <Expandable title="Patient Identifier Object">
    <ResponseField name="title" type="string">
      Identifier type name (long\_title or short\_title)
    </ResponseField>

    <ResponseField name="value" type="string">
      Identifier value
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="primary_patient_identifiers" type="object">
  Primary patient identifier based on institution settings

  <Expandable title="Primary Identifier Object">
    <ResponseField name="title" type="string">
      Primary identifier prompt/label
    </ResponseField>

    <ResponseField name="value" type="string">
      Primary identifier value
    </ResponseField>
  </Expandable>
</ResponseField>

#### Response Example

```json theme={null}
[
  {
    "id": 12345,
    "first_name": "John",
    "last_name": "Smith",
    "age": "54",
    "genderletter": "M",
    "dob": "1970-03-15",
    "is_deceased": false,
    "patient_identifiers": [
      {
        "title": "Hospital Number",
        "value": "H123456"
      },
      {
        "title": "NHS Number",
        "value": "1234567890"
      }
    ],
    "primary_patient_identifiers": {
      "title": "Hospital Number",
      "value": "H123456"
    }
  },
  {
    "id": 12346,
    "first_name": "Jane",
    "last_name": "Smith",
    "age": "Deceased",
    "genderletter": "F",
    "dob": "1945-07-22",
    "is_deceased": true,
    "patient_identifiers": [
      {
        "title": "Hospital Number",
        "value": "H789012"
      }
    ],
    "primary_patient_identifiers": {
      "title": "Hospital Number",
      "value": "H789012"
    }
  }
]
```

#### Empty Results

When no patients match the search criteria, an empty array is returned:

```json theme={null}
[]
```

HTTP Status: `200 OK`

## Implementation Details

### Search Logic

The search functionality (lines 42-91 in `PatientController.php`):

1. Retrieves search parameters from the request
2. Creates a `PatientSearch` instance
3. Validates the search term using `getValidSearchTerm()`
4. Executes the search with `PatientSearch::search()`
5. Iterates through results to build response array
6. Collects all patient identifiers
7. Determines primary identifier based on institution settings
8. Returns JSON response with status 200

### Primary Identifier Logic

When `patient_identifier_type` is NOT specified:

```php theme={null}
$primary_identifier = PatientIdentifierHelper::getIdentifierForPatient(
    SettingMetadata::model()->getSetting('display_primary_number_usage_code'),
    $patient->id,
    \Institution::model()->getCurrent()->id,
    Yii::app()->session['selected_site_id']
);
```

Source: `PatientController.php:64-69`

When `patient_identifier_type` IS specified:

```php theme={null}
$primary_identifier = null;
```

Source: `PatientController.php:61-63`

### Access Control

The controller enforces strict access control:

```php theme={null}
public function accessRules()
{
    return [
        [
            'allow',
            'actions' => ['search'],
            'users' => ['@'],  // authenticated users only
        ],
        [
            'deny',
            'users' => ['*'],
        ],
    ];
}
```

Source: `PatientController.php:20-33`

### Behaviors

```php theme={null}
public function behaviors()
{
    return array_merge(parent::behaviors(), [
        'BasicAuthBehavior' => ['class' => 'application.modules.Api.behaviors.BasicAuthBehavior'],
    ]);
}
```

Source: `PatientController.php:35-40`

## Patient Model Reference

The Patient model (`protected/models/Patient.php`) includes:

### Core Attributes

* `id` - Primary key
* `title` - Patient title (Mr, Mrs, Dr, etc.)
* `first_name` - First name
* `last_name` - Last name
* `dob` - Date of birth
* `date_of_death` - Date of death (if applicable)
* `gender` - Gender
* `hos_num` - Hospital number (legacy)
* `nhs_num` - NHS number (legacy)
* `primary_phone` - Primary phone number
* `deleted` - Soft delete flag
* `ethnic_group_id` - Ethnic group
* `patient_source` - Source of patient record
* `primary_institution_id` - Primary institution

### Related Models

* `episodes[]` - Patient episodes
* `addresses[]` - Patient addresses
* `contact` - Contact information
* `identifiers[]` - Patient identifiers (modern approach)
* `allergies[]` - Patient allergies
* `trials[]` - Clinical trials

Source: `protected/models/Patient.php:1-100`

## Usage Examples

### Search by Name

```bash theme={null}
curl -u api_user:password \
  "https://openeyes.example.com/api/v1/patient/search?term=Smith"
```

### Search by Hospital Number

```bash theme={null}
curl -u api_user:password \
  "https://openeyes.example.com/api/v1/patient/search?term=H123456"
```

### Search by NHS Number

```bash theme={null}
curl -u api_user:password \
  "https://openeyes.example.com/api/v1/patient/search?term=1234567890"
```

### Search with Specific Identifier Type

```bash theme={null}
curl -u api_user:password \
  "https://openeyes.example.com/api/v1/patient/search?term=123456&patient_identifier_type=2"
```

### Integration Example (Python)

```python theme={null}
import requests
from requests.auth import HTTPBasicAuth

class OpenEyesPatientAPI:
    def __init__(self, base_url, username, password):
        self.base_url = base_url
        self.auth = HTTPBasicAuth(username, password)
    
    def search_patients(self, term, identifier_type=None):
        url = f"{self.base_url}/api/v1/patient/search"
        params = {'term': term}
        
        if identifier_type:
            params['patient_identifier_type'] = identifier_type
        
        response = requests.get(url, params=params, auth=self.auth)
        response.raise_for_status()
        return response.json()
    
    def get_patient_by_hospital_number(self, hos_num):
        patients = self.search_patients(hos_num)
        return patients[0] if patients else None

# Usage
api = OpenEyesPatientAPI(
    'https://openeyes.example.com',
    'api_user',
    'secure_password'
)

results = api.search_patients('Smith')
for patient in results:
    print(f"{patient['first_name']} {patient['last_name']} - {patient['age']}")
```

## Error Handling

### Invalid Search Term

If the search term fails validation, an empty result set is returned:

```json theme={null}
[]
```

### Authentication Failure

Status: `401 Unauthorized`

```json theme={null}
"401 Unauthorized"
```

### Insufficient Permissions

Status: `403 Forbidden`

```json theme={null}
"403 Forbidden"
```

## Best Practices

<AccordionGroup>
  <Accordion title="Search Term Validation">
    Always provide meaningful search terms. Empty or very short terms may not return results depending on the `PatientSearch` validation rules.
  </Accordion>

  <Accordion title="Handle Empty Results">
    Always check for empty arrays in responses. Not finding a patient is a valid outcome, not an error.

    ```javascript theme={null}
    const patients = await searchPatients('term');
    if (patients.length === 0) {
      console.log('No patients found');
    }
    ```
  </Accordion>

  <Accordion title="Use Primary Identifiers">
    The `primary_patient_identifiers` field provides the institution-configured primary identifier, which should be used for display purposes.
  </Accordion>

  <Accordion title="Respect Deceased Status">
    Always check the `is_deceased` field and `age` value before displaying patient information or scheduling appointments.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Events API" icon="calendar" href="/api/events">
    Access clinical events and attachments
  </Card>

  <Card title="Authentication" icon="lock" href="/api/authentication">
    Learn about API security
  </Card>
</CardGroup>
