Skip to content

OSCAR REST Bridge API

REST-to-SOAP bridge for OSCAR EMR integration

Status: Live and tested (January 14, 2026) Version: 1.3.0 (Admission records, config centralization)


Overview

The Oscar REST Bridge is a Node.js microservice that provides a modern REST/JSON API interface to Oscar EMR's legacy SOAP web services.

Why Two Data Sources?

Operation Data Source Reason
Get patient details SOAP Full SOAP support
Search patients SOAP + DB SOAP for name, DB for phone/quick search
Register patient Database SOAP doesn't support patient creation
Get appointments SOAP Full SOAP support
Provider availability Database SOAP method is buggy (returns empty)
Clinical data SOAP Full SOAP support

Architecture

┌─────────────────────────────────────────────────────────────┐
│                     External Clients                        │
│         (VitaraVox Voice Agent, Mobile Apps)                │
└─────────────────────┬───────────────────────────────────────┘
                      │ REST/JSON + API Key
┌─────────────────────────────────────────────────────────────┐
│                  Oscar REST Bridge (:3000)                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    Middleware                        │   │
│  │  [Rate Limiter] → [Auth] → [Logger] → [Routes]      │   │
│  └─────────────────────────────────────────────────────┘   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    Services                          │   │
│  │  DemographicService  ScheduleService  ProviderService│  │
│  └───────────────┬─────────────────────┬───────────────┘   │
│                  │                     │                    │
│  ┌───────────────┴───────────────┐ ┌──┴──────────────────┐ │
│  │         SOAP Client           │ │  Database Service    │ │
│  │  (WS-Security, Retry Logic)   │ │  (Direct MySQL)      │ │
│  └───────────────┬───────────────┘ └──────────┬──────────┘ │
│                  │                            │             │
└──────────────────┼────────────────────────────┼─────────────┘
                   │ SOAP/XML                   │ SQL
                   ▼                            ▼
┌─────────────────────────────────┐ ┌─────────────────────────┐
│      Oscar EMR (:8080)          │ │   MariaDB (:3306)       │
│   SOAP Web Services             │ │   oscar database        │
└─────────────────────────────────┘ └─────────────────────────┘

Connection Details

Setting Value
Base URL http://15.222.50.48:3000/api/v1
Authentication X-API-Key header
Timeout 30 seconds

Quick Start

# Health Check (no auth required)
curl http://15.222.50.48:3000/api/v1/health

# Search Patient by Phone
curl -H "X-API-Key: YOUR_API_KEY" \
     "http://15.222.50.48:3000/api/v1/demographics/search?phone=2367770690"

# Get Provider Availability
curl -H "X-API-Key: YOUR_API_KEY" \
     "http://15.222.50.48:3000/api/v1/appointments/availability?providerId=100&date=2026-01-14"

Endpoints

Health Check

GET /api/v1/health

No authentication required.

Response:

{
  "status": "healthy",
  "timestamp": "2026-01-13T15:30:00Z",
  "services": {
    "soap": "connected",
    "database": "connected"
  }
}


Demographics (Patients)

Multi-field search across name, phone, HIN, chart number.

GET /api/v1/demographics/quickSearch?term={searchTerm}&limit={limit}
Parameter Type Required Description
term string Yes Search term (min 2 chars)
limit number No Max results (default 20, max 100)

Search by Name or Phone

GET /api/v1/demographics/search?name={lastName}&firstName={firstName}
GET /api/v1/demographics/search?phone={phoneNumber}

Response:

{
  "success": true,
  "data": [
    {
      "demographicNo": 78,
      "firstName": "Charles",
      "lastName": "Don",
      "dateOfBirth": "1985-06-15",
      "sex": "M",
      "phone": "+12367770690"
    }
  ]
}

Get Patient by ID

GET /api/v1/demographics/:id

Register New Patient (v1.3.0)

POST /api/v1/demographics

Request Body:

{
  "firstName": "Jane",
  "lastName": "Doe",
  "dateOfBirth": "1990-05-20",
  "gender": "female",
  "phone": "+12367770690",
  "email": "jane@example.com",
  "address": "123 Main St",
  "city": "Vancouver",
  "province": "BC",
  "postalCode": "V6B 1A1",
  "healthCardNumber": "1234567890"
}

Response:

{
  "success": true,
  "data": {
    "demographicNo": 79,
    "admissionId": 71,
    "programId": 10034
  }
}

v1.3.0 Features: - Creates admission record automatically linking patient to OSCAR Service program - Patient appears in provider rosters immediately - Uses OSCAR_DEFAULT_PROGRAM_ID (default: 10034) for admission program - Direct database insert (SOAP doesn't support patient creation)


Providers

List Providers

GET /api/v1/providers

Response:

{
  "success": true,
  "data": [
    {
      "providerNo": "100",
      "firstName": "Sarah",
      "lastName": "Anderson",
      "specialty": "Family Medicine"
    }
  ]
}


Appointments

Get Provider Availability (v1.2.0)

Returns available time slots based on provider's schedule template.

GET /api/v1/appointments/availability?providerId={id}&date={YYYY-MM-DD}
Parameter Type Required Description
providerId string Yes Provider number (e.g., "100")
date date Yes Date to check (YYYY-MM-DD)

Response (with schedule):

{
  "success": true,
  "data": {
    "providerId": "100",
    "date": "2026-01-14",
    "available": true,
    "holiday": false,
    "timeSlots": [
      {
        "startTime": "09:00",
        "endTime": "09:15",
        "status": "available",
        "code": "B",
        "description": "Behavioral Science",
        "duration": 15
      },
      {
        "startTime": "09:15",
        "endTime": "09:30",
        "status": "available",
        "code": "B",
        "description": "Behavioral Science",
        "duration": 15
      }
    ]
  }
}

Response (no schedule configured):

{
  "success": true,
  "data": {
    "providerId": "129",
    "date": "2026-01-14",
    "timeSlots": []
  }
}

Implementation Note: This endpoint queries the database directly because Oscar's SOAP getDayWorkSchedule method is buggy and returns empty results even when schedule data exists.

Get Appointment Types

GET /api/v1/appointments/types

Response:

{
  "success": true,
  "data": [
    { "code": "B", "name": "Behavioral Science", "duration": 15 },
    { "code": "2", "name": "New Concern", "duration": 30 },
    { "code": "3", "name": "Annual Physical", "duration": 45 },
    { "code": "P", "name": "Phone Consultation", "duration": 15 }
  ]
}

List Appointments

GET /api/v1/appointments?providerId={id}&startDate={YYYY-MM-DD}&endDate={YYYY-MM-DD}

Create Appointment

POST /api/v1/appointments

Request Body:

{
  "demographicId": 78,
  "providerId": "100",
  "appointmentDate": "2026-01-15",
  "startTime": "09:00",
  "duration": 15,
  "reason": "Follow-up"
}

Update Appointment

PUT /api/v1/appointments/:id

Cancel Appointment

DELETE /api/v1/appointments/:id

Note: Oscar doesn't support true deletion. This sets status to 'C' (Cancelled).


Oscar Schedule Pattern Encoding

Oscar uses character patterns for provider schedules:

// Oscar Schedule Pattern Encoding
// Each character = 15 minute slot
// '_' = unavailable
// 'B', '1', '2', etc. = available (template code)

// Example 48-char pattern (clinic hours, 8 AM start):
"____BBBBBBBBBBBB________BBBBBBBBBBBB____________"
// └──┘ 8-9 AM unavailable
//     └────────────┘ 9 AM - 12 PM available
//                   └────────┘ 12-2 PM lunch
//                             └────────────┘ 2-5 PM available
//                                           └────────────┘ 5-8 PM unavailable

Pattern Details: - 48 chars = 12-hour day (8 AM - 8 PM) - 96 chars = 24-hour day (midnight start) - Each character = 15-minute slot - _ = unavailable - Letters = appointment type code from scheduletemplatecode table


Configuration (v1.3.0)

Environment variables for customizing bridge behavior:

Variable Default Purpose
OSCAR_DEFAULT_PROGRAM_ID 10034 Admission program ID for new patients
OSCAR_DEFAULT_PROVIDER_ID (empty) Fallback provider for admissions
OSCAR_DEFAULT_PROVINCE BC Default patient province
OSCAR_DEFAULT_COUNTRY CA Default patient country
OSCAR_DEFAULT_LANGUAGE English Default patient language
OSCAR_CHART_PREFIX NEW Chart number prefix for new patients

Database Configuration:

Variable Default Purpose
DB_HOST localhost MariaDB host
DB_PORT 3306 MariaDB port
DB_USER root Database user
DB_PASSWORD - Database password
DB_NAME oscar Database name
DB_CONNECTION_LIMIT 10 Connection pool size

Endpoint Summary (v1.3.0)

GET  /api/v1/health                                         ← v1.3.0 (singleton, SOAP+DB check)

GET  /api/v1/demographics
GET  /api/v1/demographics/:id
GET  /api/v1/demographics/search?name=xxx OR ?phone=xxx
GET  /api/v1/demographics/quickSearch?term=xxx
POST /api/v1/demographics                                   ← v1.3.0 (creates admission record)

GET  /api/v1/appointments?providerId=&startDate=&endDate=
GET  /api/v1/appointments/availability?providerId=&date=    ← v1.2.0 (DB direct)
GET  /api/v1/appointments/types
POST /api/v1/appointments
PUT  /api/v1/appointments/:id
DELETE /api/v1/appointments/:id

GET  /api/v1/providers
GET  /api/v1/providers/current
GET  /api/v1/allergies
GET  /api/v1/prescriptions
GET  /api/v1/measurements

Known Issues & Workarounds

SOAP getDayWorkSchedule Bug

Issue: Oscar's SOAP getDayWorkSchedule method returns empty timeSlots even when schedule data exists in the database.

Workaround: The /appointments/availability endpoint queries the scheduledate and scheduletemplatecode tables directly.

// SOAP returns this (buggy):
{ holiday: false, timeSlotDurationMin: 15, timeSlots: [] }

// Database has actual data in scheduledate.hour:
"____BBBBBBBBBBBB________BBBBBBBBBBBB____________"

No SOAP Patient Creation

Issue: Oscar's SOAP DemographicWs service doesn't provide a method to create new patients.

Workaround: POST /demographics inserts directly into the demographic table.


Test Data

ID Name Phone Provider
78 Charles Don +12367770690 -
100 Sarah Anderson - Provider with schedule

Security

Feature Implementation
API Authentication X-API-Key header
Rate Limiting 100 req/15 min per API key
SQL Injection Parameterized queries
Input Validation Route-level checks

Infrastructure (v1.3.0)

Health Check Improvements

  • Singleton services: SOAP client and database service are singleton instances (no longer created per request)
  • Dual connectivity check: Health endpoint verifies both SOAP and database connections
  • Response format: Returns service status for each backend
{
  "status": "healthy",
  "timestamp": "2026-01-14T00:00:00Z",
  "services": {
    "soap": "connected",
    "database": "connected"
  }
}

Graceful Shutdown

The bridge handles SIGTERM/SIGINT signals for clean shutdown:

  1. Receives shutdown signal
  2. Logs: "SIGTERM received, shutting down gracefully"
  3. Closes database connection pool
  4. Logs: "Closing database connection pool"
  5. Exits cleanly

This ensures: - No orphaned database connections - Clean container restarts in Docker/Kubernetes - No data corruption during deployments

Files Modified (v1.3.0)

File Changes
src/config/environment.js Added database config and Oscar defaults
src/services/database-service.js Added createAdmission(), uses centralized config
src/transformers/demographic-transformer.js Uses config for country/province defaults
src/index.js Singleton services, improved health check, graceful shutdown

Error Codes

Code HTTP Status Description
UNAUTHORIZED 401 Missing or invalid API key
MISSING_PARAMETERS 400 Required query parameters missing
INVALID_DATE_FORMAT 400 Date must be YYYY-MM-DD
DUPLICATE_PATIENT 409 Patient already exists
NOT_FOUND 404 Resource not found
SOAP_ERROR 502 Oscar SOAP service error
SERVICE_UNAVAILABLE 503 Backend service unreachable