Skip to main content

Overview

The Events API provides endpoints for managing clinical events, digital signatures, and event attachments. This includes integration with external devices and systems for importing clinical data.

API Modules

The Events API is split across multiple modules:
  • Sign Module: Digital signature capture and validation
  • Request Module: External data integration and queue management
  • Attachment Module: Event attachment display and management

Event Model Reference

The Event model (protected/models/Event.php) represents clinical events:

Core Attributes

  • id - Event unique identifier
  • episode_id - Associated episode ID
  • user_id - User who created the event
  • event_type_id - Type of clinical event
  • info - Additional event information
  • deleted - Soft delete flag
  • delete_reason - Reason for deletion
  • is_automated - Whether event was auto-generated
  • automated_source - JSON structure describing automation source
  • event_date - Date of the clinical event
  • created_date - Record creation timestamp
  • last_modified_date - Last modification timestamp
  • firm_id - Associated firm/service
  • parent_id - Parent event (for linked events)
  • episode - Patient episode
  • eventType - Event type definition
  • user - Creating user
  • eventAttachmentGroups[] - Attached files/images
  • parent - Parent event
  • children[] - Child events
Source: protected/models/Event.php:1-100

Digital Signatures API

Add Signature

Import a digital signature for a clinical event.
POST /api/v1/sign/add
Content-Type: application/json

Request Body

unique_identifier
string
required
Unique code identifying the event. Validated using UniqueCodes::eventFromUniqueCode().
extra_info
string
required
JSON string containing element information:
  • e_id (integer, required) - Element ID
  • e_t_id (integer, required) - Element type ID
Example: {"e_id": 123, "e_t_id": 456}
image
string
required
Base64-encoded signature image (PNG format recommended)
origImage
string
Base64-encoded original signature image before cropping
original_log_id
integer
For manual crop operations, references the original import log ID

Response

Success Response:
"Correct signature saved!"
HTTP Status: 200 OK Error Response:
"Failed signature save!"
HTTP Status: 200 OK (note: error returned with 200 status) Bad Request:
"Bad request"
HTTP Status: 400 Bad Request

Validation Errors

The endpoint validates:
  1. Missing unique_identifier: "Missing unique_identifier"
  2. Missing extra_info: "Missing element and element type ids"
  3. Missing e_id in extra_info: "Missing element id."
  4. Missing e_t_id in extra_info: "Missing element type id."
  5. Event not found: "Event not found"
  6. Invalid unique code: "Invalid UniqueCode or wrong event"
Source: protected/modules/Api/controllers/SignController.php:52-95

Access Control

The Sign API allows access to all users ('users' => ['*']). No authentication is required for the /sign/add endpoint. This is intentional for external signature capture devices.
Source: SignController.php:37-49

CORS Support

The endpoint includes CORS headers:
header('Access-Control-Allow-Origin: *');
Source: SignController.php:142

Implementation Details

The signature import process (lines 140-209):
  1. Accepts POST request with JSON body
  2. Validates unique identifier and retrieves event
  3. Validates request data structure
  4. Saves signature image to protected file storage
  5. Creates signature element and associates with event
  6. Logs import attempt with status (success/failure)
  7. Updates CVI event information if applicable
  8. Returns success or error message

Signature File Storage

Signatures are stored using the ProtectedFile model:
public function saveToProtectedFile($data)
{
    $filename = "sign_" . md5(time()) . ".png";
    $protected_file = \ProtectedFile::createForWriting($filename);
    file_put_contents($protected_file->getPath(), base64_decode($data['origImage']??$data['image']));
    $protected_file->title = "";
    $protected_file->description = "";
    $protected_file->validate();
    $protected_file->save(false);
    return $protected_file;
}
Source: SignController.php:102-112

Signature Import Logging

All signature import attempts are logged:
public function saveSignatureImportLog($protected_file, $status_id, $event = null, $return_message = '', $cropped_file_id = null)
{
    $signature_import_log = new SignatureImportLog();
    $signature_import_log->filename = $protected_file->getPath();
    $signature_import_log->cropped_file_id = $cropped_file_id;
    $signature_import_log->status_id = $status_id;
    $signature_import_log->return_message = $return_message;
    $signature_import_log->event_id = $event->id ?? null;
    $signature_import_log->import_datetime = date('Y-m-d H:i:s');
    $signature_import_log->save(false);
    return true;
}
Source: SignController.php:119-130

Event Attachments API

View Attachment

Retrieve an event attachment by ID.
GET /api/v1/attachmentdisplay/view?id={id}&mime={mime_type}&attachment={field}

Query Parameters

id
integer
required
Attachment data ID from the attachment_data table
mime
string
MIME type for Content-Type header (e.g., image/png, image/jpeg, application/pdf)
attachment
string
Field name in the AttachmentData model containing the binary data

Response

Returns the raw attachment data with appropriate headers:
HTTP/1.1 200 OK
Content-Type: image/png
Cache-Control: private, max-age=31536000, immutable
Content-Transfer-Encoding: binary
Last-Modified: Mon, 09 Mar 2026 10:30:00 GMT
Content-Length: 45678

[binary data]

Caching

The endpoint implements conditional GET with ETags:
  • Returns 304 Not Modified if client cache is current
  • Uses If-Modified-Since header for validation
  • Sets immutable cache for 1 year (attachments are versioned by ID)
Source: AttachmentDisplayController.php:53-93

Authentication

Requires authenticated user:
public function accessRules()
{
    return array(
        array(
            'allow',
            'actions' => array('view', 'create'),
            'users' => array('@'),
        ),
        array(
            'deny',
            'users' => array('*'),
        ),
    );
}
Source: AttachmentDisplayController.php:38-50

Request Queue API

Add to Queue

Submit external device data to the processing queue.
POST /api/v1/request/queue/add
Content-Type: application/json

Request Body

request_type
string
Type of request being submitted (e.g., “diagnostic_data”, “imaging”, “form_data”)
system_message
string
Message from the external system describing the data
data
object
required
The actual data payload. Structure depends on content type and request handler.

Content Types Supported

The endpoint supports multiple content types:
  • application/json
  • multipart/form-data
  • application/x-www-form-urlencoded
Handlers are dynamically loaded based on Content-Type header. Source: QueueController.php:26-30

Response

Success Response:
{
  "request_id": 789
}
HTTP Status: 200 OK Validation Error Response:
{
  "success": 0,
  "message": {
    "field_name": "Error description"
  }
}
HTTP Status: 422 Unprocessable Entity System Error Response:
{
  "success": 0,
  "message": {
    "system": "Exception message"
  }
}
HTTP Status: 500 Internal Server Error Source: QueueController.php:36-48

Request Processing Flow

  1. Determine content type from header or GET parameter
  2. Instantiate appropriate handler class dynamically
  3. Handler validates and processes request data
  4. Data saved to queue for async processing
  5. Return request ID or error details
Source: QueueController.php:21-51

Event Attachment Groups

Event attachments are organized in groups associated with event elements.

EventAttachmentGroup Model

Attributes:
  • id - Group identifier
  • event_id - Associated event ID
  • element_type_id - Element type this group belongs to
Relations:
  • event - Parent event
  • elementType - Element type definition
  • eventAttachmentItems[] - Individual attachment items in this group
Source: protected/modules/Api/modules/Request/models/EventAttachmentGroup.php:17-79

Example: Retrieving Event Attachments

// Get event
$event = Event::model()->findByPk($event_id);

// Get all attachment groups
foreach ($event->eventAttachmentGroups as $group) {
    echo "Element Type: " . $group->elementType->name . "\n";
    
    // Get items in this group
    foreach ($group->eventAttachmentItems as $item) {
        echo "Attachment: " . $item->attachment_data_id . "\n";
    }
}

URL Routing

Main API Routes

['Api/<controller>/<action>', 'pattern' => 'api/v1/<controller>/<action>', 'verb' => 'GET, POST, PUT']
Source: protected/modules/Api/config/common.php:21

Request Module Routes

['Api/Request/<controller>/<action>', 'pattern' => 'api/v1/request/<controller:\w+>/<action>', 'verb' => 'GET, POST, PUT']
Source: protected/modules/Api/modules/Request/config/common.php

Integration Examples

Python: Import Signature

import requests
import base64
import json

def import_signature(image_path, unique_code, element_id, element_type_id):
    # Read and encode image
    with open(image_path, 'rb') as f:
        image_data = base64.b64encode(f.read()).decode('utf-8')
    
    # Prepare request
    url = 'https://openeyes.example.com/api/v1/sign/add'
    payload = {
        'unique_identifier': unique_code,
        'extra_info': json.dumps({
            'e_id': element_id,
            'e_t_id': element_type_id
        }),
        'image': image_data
    }
    
    # Send request
    response = requests.post(url, json=payload)
    return response.text

result = import_signature(
    'signature.png',
    'ABC123XYZ',
    element_id=456,
    element_type_id=12
)
print(result)

JavaScript: Queue External Data

async function queueDeviceData(deviceData) {
  const credentials = btoa('username:password');
  
  const response = await fetch('https://openeyes.example.com/api/v1/request/queue/add', {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${credentials}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      request_type: 'oct_scan',
      system_message: 'OCT data from Device XYZ',
      data: deviceData
    })
  });
  
  const result = await response.json();
  
  if (result.request_id) {
    console.log(`Queued with ID: ${result.request_id}`);
  } else {
    console.error('Error:', result.message);
  }
}

Best Practices

Always validate the unique_identifier is correct before submitting signatures. Invalid codes cannot be corrected after submission.
Ensure signature images are properly base64-encoded PNG format for best compatibility.
Check both HTTP status codes and response content. Some endpoints return error messages with 200 status.
Leverage the If-Modified-Since header when repeatedly accessing the same attachment to reduce bandwidth.
Data submitted to the queue is processed asynchronously. Implement polling or webhooks to check processing status.

Next Steps

Patient API

Search for patients to associate with events

Authentication

Secure your API requests

Build docs developers (and LLMs) love