OSCAR API Protocol Analysis¶
Last Updated: 2026-03-09
Status: PRODUCTION — REST protocol extension deployed, 98 unit + 8 integration tests
Executive Summary¶
OSCAR EMR exposes three separate API surfaces with different authentication, content types, and deployment conditions. Our OscarSoapAdapter currently uses Surface 1 (SOAP) for 9 of 11 methods. For Kai-hosted instances behind Cloudflare WAF, SOAP is blocked — only Surface 3 (OAuth REST) works. This analysis documents every API surface, every adapter method, and the proven Cloudflare behavior, informing the preferRest protocol extension.
The Three API Surfaces¶
OSCAR runs three API namespaces on the same Tomcat instance:
OSCAR EMR Server (Tomcat)
┌────────────────────────────────────────────────────────────────────────┐
│ │
│ SURFACE 1: SOAP Web Services │
│ ───────────────────────────── │
│ Path: /oscar/ws/{ServiceName} │
│ Proto: SOAP 1.1 XML │
│ Auth: WS-Security UsernameToken (username + password in SOAP hdr) │
│ C-Type: text/xml │
│ Deploy: ALWAYS present (applicationContext.xml, unconditional) │
│ Count: 14 services (Provider, Demographic, Schedule, Booking, ...) │
│ │
│ SURFACE 2: Cookie-Authenticated REST │
│ ──────────────────────────────────── │
│ Path: /oscar/ws/rs/{resource} │
│ Proto: REST (JSON) │
│ Auth: JSESSIONID cookie — OR — OAuth 1.0a signed request │
│ C-Type: application/json │
│ Deploy: ALWAYS present (applicationContext.xml, unconditional) │
│ Count: 250+ endpoints (190KB WADL) │
│ │
│ SURFACE 3: OAuth-Protected REST │
│ ──────────────────────────────── │
│ Path: /oscar/ws/services/{resource} │
│ Proto: REST (JSON) │
│ Auth: OAuth 1.0a HMAC-SHA1 (consumer key + access token) │
│ C-Type: application/json │
│ Deploy: CONDITIONAL — only when ModuleNames=REST in oscar.properties │
│ Count: 30+ endpoint beans behind OAuthInterceptor │
│ Also: /oscar/ws/oauth/* (OAuth token lifecycle: initiate, │
│ authorize, token) — deployed by same module │
│ │
└────────────────────────────────────────────────────────────────────────┘
Why Three Surfaces Exist¶
OSCAR was built SOAP-first (Surface 1). The /ws/rs/ REST endpoints (Surface 2) came later for internal OSCAR UI use — they authenticate via browser session cookies. The /ws/services/ OAuth REST endpoints (Surface 3) were added specifically for third-party integrations like Cortico, gated behind ModuleNames=REST so clinics only deploy them when they need external API access.
The ModuleNames=REST Gate¶
oscar.properties:
#ModuleNames=REST ← COMMENTED OUT by default in stock OSCAR
OscarSpringContextLoader.java:
String moduleNames = OscarProperties.getInstance().getProperty("ModuleNames");
if (moduleNames != null) {
for (String module : moduleNames.split(",")) {
contextLoader.loadContext("applicationContext" + module.trim() + ".xml");
}
}
applicationContextREST.xml: ← ONLY loaded when ModuleNames includes "REST"
<jaxrs:server id="oauthService" address="/oauth"> → deploys /ws/oauth/*
<jaxrs:server id="restServices" address="/services"> → deploys /ws/services/*
- Without
ModuleNames=REST: CXF listing at/oscar/ws/shows 14 SOAP + 1 REST (/ws/rs) - With
ModuleNames=REST: CXF listing shows 14 SOAP + 3 REST (/ws/rs,/ws/services,/ws/oauth)
Server Inventory¶
| Property | Dev OSCAR | Kai OSCAR Pro (FreshBay) |
|---|---|---|
| Host | 15.222.50.48:8443 |
fbh.kai-oscar.com |
| Build | OpenOSP (open source) | Kai OSCAR Pro (WELL Health) |
| Infrastructure | AWS EC2 (t3a.medium) | WELL Health cloud |
| Cloudflare | No | Yes (WAF active, YYZ PoP) |
| ModuleNames=REST | SET (all 3 REST surfaces) | NOT SET (only /ws/rs) |
| SOAP | All 14 services work | CF blocks SOAP envelope |
| Use case | Development + testing | First production customer |
Cloudflare WAF Behavior¶
CF blocks SOAP requests to Kai-hosted OSCAR
The Cloudflare WAF on Kai-hosted instances uses content-inspection to block SOAP requests. Any request with Content-Type: text/xml AND a body containing <soapenv:Envelope is blocked with HTTP 403. REST requests with Content-Type: application/json are never blocked.
CF Decision Logic¶
Incoming Request to fbh.kai-oscar.com
│
▼
┌──────────────────────────┐
│ Cloudflare WAF │
│ │
│ Content-Type = text/xml │──NO──► PASS to OSCAR
│ ? │
└───────────┬──────────────┘
│ YES
▼
┌──────────────────────────┐
│ Body contains │
│ <soapenv:Envelope ? │──NO──► PASS to OSCAR
│ │
└───────────┬──────────────┘
│ YES
▼
┌──────────────┐
│ HTTP 403 │
│ CF blocks │
│ Kai HTML err │
└──────────────┘
The 8-Test Proof Matrix¶
All tests run from 132.145.100.100 (our OCI server) on 2026-02-26:
| # | URL Path | Content-Type | Body | HTTP | Origin |
|---|---|---|---|---|---|
| A | /ws/ProviderService |
text/xml | SOAP envelope | 403 | CF BLOCK |
| F | /ws/DemographicService |
text/xml | SOAP envelope | 403 | CF BLOCK |
| G | /ws/ProviderService |
text/xml | plain XML (no SOAP) | 500 | OSCAR |
| E | /ws/ProviderService |
text/xml | (empty body) | 500 | OSCAR |
| B | /ws/ProviderService |
application/json | JSON | 500 | OSCAR |
| H | /ws/ProviderService |
application/json | JSON w/ "soap" string | 500 | OSCAR |
| C | /ws/services/schedule/add |
application/json | JSON | 404 | OSCAR |
| D | /ws/services/schedule/add |
text/xml | XML fragment | 404 | OSCAR |
Key conclusions:
- Tests A+F: SOAP envelope on SOAP path — CF blocks (403)
- Tests G+E: Same SOAP path but no SOAP envelope — CF passes (500 = OSCAR error)
- Tests B+H: Same SOAP path but JSON — CF passes (500 = OSCAR error)
- Tests C+D: REST path with JSON or XML — CF passes (404 = REST module not loaded)
- Our REST calls use
Content-Type: application/json— will NEVER be blocked by CF.
quickSearch Parameter¶
Correct parameter name
OSCAR CXF uses @QueryParam("query") annotation. The correct URL is ?query=SEARCHTERM. Using ?searchString or ?term returns {query: null, total: 0} — not an error, just empty results. Validated on both dev OSCAR and Kai (2026-03-01).
warmUp and Cold TLS¶
For preferRest adapters connecting to Kai OSCAR (Cloudflare-fronted), the first TLS handshake takes ~6 seconds (CF certificate exchange + connection pooling). The circuit breaker timeout is 4 seconds. Without pre-warming the adapter, the first production call after cache miss will trip the breaker.
Fix: EmrAdapterFactory now awaits warmUp() for preferRest adapters. PM2 startup also warms all known preferRest clinics via IIFE in index.ts.
Current Adapter: Per-Method Analysis¶
OscarSoapAdapter.ts — 1,298 lines, 11 public methods. Here is every method's protocol, URL, auth, and per-server behavior:
Request Details¶
| # | Method | Protocol | URL | HTTP | C-Type | Auth |
|---|---|---|---|---|---|---|
| 1 | getProviders() |
SOAP | /ws/ProviderService |
POST | text/xml | WS-Security |
| 2 | getProvider(id) |
SOAP | (delegates to #1) | POST | text/xml | WS-Security |
| 3 | getScheduleSlots() |
SOAP | /ws/ScheduleService |
POST | text/xml | WS-Security |
| 4 | getScheduleTemplateCodes() |
SOAP | /ws/ScheduleService |
POST | text/xml | WS-Security |
| 5 | getAppointments() |
SOAP | /ws/ScheduleService |
POST | text/xml | WS-Security |
| 6 | createAppointment() |
SOAP | /ws/ScheduleService |
POST | text/xml | WS-Security |
| 7 | cancelAppointment() |
SOAP | /ws/ScheduleService |
POST | text/xml | WS-Security |
| 8 | getPatient(id) |
SOAP | /ws/DemographicService |
POST | text/xml | WS-Security |
| 9 | searchPatient(name) |
SOAP | /ws/DemographicService |
POST | text/xml | WS-Security |
| 10 | searchPatient(phone) |
REST | /ws/rs/demographic/search |
GET | app/json | OAuth 1.0a |
| 11 | createPatient() |
REST | /ws/services/demographics |
POST | app/json | OAuth 1.0a |
Per-Server Status (Current)¶
| # | Method | Dev OSCAR | Kai FreshBay |
|---|---|---|---|
| 1 | getProviders() |
:white_check_mark: Works | :x: CF blocks SOAP envelope |
| 2 | getProvider(id) |
:white_check_mark: Works | :x: CF blocks |
| 3 | getScheduleSlots() |
:white_check_mark: Works | :x: CF blocks SOAP envelope |
| 4 | getScheduleTemplateCodes() |
:white_check_mark: Works | :x: CF blocks SOAP envelope |
| 5 | getAppointments() |
:white_check_mark: Works | :x: CF blocks SOAP envelope |
| 6 | createAppointment() |
:white_check_mark: Works | :x: CF blocks SOAP envelope |
| 7 | cancelAppointment() |
:white_check_mark: Works | :x: CF blocks SOAP envelope |
| 8 | getPatient(id) |
:white_check_mark: Works | :x: CF blocks SOAP envelope |
| 9 | searchPatient(name) |
:white_check_mark: Works | :x: CF blocks SOAP envelope |
| 10 | searchPatient(phone) |
:white_check_mark: Works | :warning: CF passes, auth unclear |
| 11 | createPatient() |
:white_check_mark: Works | :x: 404 (REST module not deployed) |
Scorecard
Dev OSCAR: 11/11 methods work. Kai FreshBay: 0/11 methods work for production use.
The Fix: preferRest Flag¶
What Changes¶
Add preferRest: boolean to OscarSoapAdapter. When true, all 11 methods route to /ws/services/* (Surface 3) using OAuth 1.0a signed JSON requests instead of SOAP XML.
Implemented Routing (Production)¶
| # | Method | preferRest=false (DEFAULT) | preferRest=true (Kai) |
|---|---|---|---|
| 1 | getProviders() |
SOAP /ws/ProviderService |
GET /ws/services/providerService/providers (3-tier fallback: JSON→XML→DB) |
| 2 | getProvider(id) |
delegates to #1 + filter | delegates to #1 + filter |
| 3 | getScheduleSlots() |
SOAP /ws/ScheduleService |
GET /ws/services/schedule/{id}/day/{date} |
| 4 | getScheduleTemplateCodes() |
SOAP /ws/ScheduleService |
GET /ws/services/schedule/codes (fallback: /scheduleTemplate) |
| 5 | getAppointments() |
SOAP /ws/ScheduleService |
GET /ws/services/schedule/{id}/day/{date} (iterated per day) |
| 6 | createAppointment() |
SOAP /ws/ScheduleService |
POST /ws/services/schedule/add (NewAppointmentTo1 format) |
| 7 | cancelAppointment() |
SOAP /ws/ScheduleService |
POST /ws/services/schedule/appointment/{id}/updateStatus (status='C') |
| 8 | getPatient(id) |
SOAP /ws/DemographicService |
GET /ws/services/demographics/{id} |
| 9 | searchPatient(name) |
SOAP /ws/DemographicService |
GET /ws/services/demographics/quickSearch?query={term} |
| 10 | searchPatient(phone) |
REST /ws/rs/demographic/search |
GET /ws/services/demographics/quickSearch?query={phone} (fallback: /ws/rs/) |
| 11 | createPatient() |
REST /ws/services/demographics |
POST /ws/services/demographics (unchanged) |
REST API Gotchas (Validated in Production)
- quickSearch: Parameter is
?query=(CXF@QueryParam("query")), NOT?searchString=or?term=. Wrong param silently returns{query: null, total: 0}. - schedule/add: Expects
NewAppointmentTo1body, NOTAppointmentTo1. Key:startTimeis "HH:mm" (no seconds),durationis required (endTime calculated server-side),statusmust not be null (causes NPE). - cancelAppointment: Endpoint is
/schedule/appointment/{id}/updateStatus, NOT/schedule/updateAppointment. - Provider JSON 406: JAXB bug returns 406 for JSON. Fallback: request XML with
Accept: application/xml, then parse XML. Further fallback: direct DB query viaClinicProvidertable.
Per-Server Status AFTER preferRest¶
| # | Method | Dev OSCAR | Kai (after ModuleNames=REST) |
|---|---|---|---|
| 1-11 | All methods | :white_check_mark: 11/11 | :white_check_mark: 11/11 |
Prerequisite
Kai must enable ModuleNames=REST in oscar.properties and restart Tomcat. Without that, Kai scores 0/11 regardless of our code changes.
Request Lifecycle: Before vs After¶
BEFORE: getProviders() on Kai (FAILS)¶
OscarSoapAdapter.getProviders()
│
▼
node-soap builds SOAP XML envelope:
┌─────────────────────────────────────────────────────────┐
│ POST /oscar/ws/ProviderService HTTP/1.1 │
│ Host: fbh.kai-oscar.com │
│ Content-Type: text/xml; charset=utf-8 │
│ │
│ <soapenv:Envelope xmlns:soapenv="..."> │
│ <soapenv:Header> │
│ <wsse:Security mustUnderstand="1"> │
│ <wsse:UsernameToken> │
│ <wsse:Username>vitara</wsse:Username> │
│ <wsse:Password>FreshBay999</wsse:Password> │
│ </wsse:UsernameToken> │
│ </wsse:Security> │
│ </soapenv:Header> │
│ <soapenv:Body> │
│ <ws:getProviders2><arg0>true</arg0></ws:getProviders│
│ </soapenv:Body> │
│ </soapenv:Envelope> │
└──────────────────────┬──────────────────────────────────┘
│
▼
Cloudflare WAF (YYZ PoP)
┌────────────────────────┐
│ text/xml? YES │
│ SOAP envelope? YES │
│ → BLOCK │
└──────────┬─────────────┘
│
▼
HTTP 403 + Kai HTML error page
(never reaches OSCAR)
AFTER: getProviders() on Kai with preferRest=true (WORKS)¶
OscarSoapAdapter.getProviders()
│
│ preferRest=true → oauthRestCall()
▼
OAuth 1.0a signs the request (HMAC-SHA1):
┌──────────────────────────────────────────────────────────┐
│ GET /oscar/ws/services/providerService/providers HTTP/1.1│
│ Host: fbh.kai-oscar.com │
│ Content-Type: application/json │
│ Authorization: OAuth oauth_consumer_key="3stz8h7nah...", │
│ oauth_token="<access_token>", │
│ oauth_signature_method="HMAC-SHA1", │
│ oauth_signature="<computed>", ... │
└──────────────────────┬───────────────────────────────────┘
│
▼
Cloudflare WAF (YYZ PoP)
┌────────────────────────┐
│ text/xml? NO (app/json)│
│ → PASS │
└──────────┬─────────────┘
│
▼
OSCAR Tomcat
┌────────────────────────┐
│ /ws/services/ matched │
│ OAuthInterceptor │
│ validates signature │
│ → returns JSON │
└──────────┬─────────────┘
│
▼
JSON response → transformProvider() → AdapterResult
Authentication Comparison¶
| Aspect | SOAP (WS-Security) | REST (OAuth 1.0a) |
|---|---|---|
| Where credentials go | Inside SOAP XML header | HTTP Authorization header |
| Credential type | Username + plaintext password | Consumer key+secret + token+secret |
| Signature | None (PasswordText) | HMAC-SHA1 over method+url+params |
| Replay protection | None (no timestamp/nonce) | Timestamp + nonce in every request |
| Setup needed | _ws_ role on OSCAR user |
OAuth client + 3-legged auth flow |
| Token lifecycle | Password never expires | Access token (TTL=365000) |
| CF WAF interaction | text/xml triggers block | app/json never triggers block |
| Security posture | Plaintext password in XML over TLS | Signed requests, no shared secret in transit |
Every Auth Method Tested¶
We tested 10 authentication approaches against Kai FreshBay. Only one path forward:
| # | Auth Method | Verdict | Why |
|---|---|---|---|
| 1 | OAuth 1.0a 3-legged REST | :white_check_mark: VIABLE | The Cortico model. Needs ModuleNames=REST. |
| 2 | SOAP WS-Security | :x: | CF blocks SOAP envelope + 500 on WS-Security |
| 3 | OAuth 2.0 / SMART FHIR | :x: | 404 — not deployed (browser-verified) |
| 4 | 2-Legged OAuth | :x: | OSCAR code rejects (preAuthorizedToken always null) |
| 5 | Basic Auth | :x: | 401 — not supported |
| 6 | Bearer Token | :x: | 401 — ignored by OSCAR |
| 7 | API Key Header | :x: | 401 — not recognized |
| 8 | Session Cookie | :x: | CF blocks /login.do, LoginService returns 500 |
| 9 | Negotiate/NTLM | :x: | 401 — not supported |
| 10 | Direct DB Token Insert | :x: | Needs MySQL access (unavailable) + still needs REST module |
Auth Method Elimination Funnel¶
AUTH METHOD ELIMINATION (Kai OSCAR Pro)
┌──────────────────────────────────────────────────────────────┐
│ 10 Authentication Methods Tested Against Kai FreshBay │
├──────────────────────────────────────────────────────────────┤
│ │
│ SOAP WS-Security ──────────► BLOCKED by CF WAF │
│ (Content-Type: text/xml) (content-inspection rule) │
│ │
│ Session Cookie ────────────► CF blocks /login.do │
│ (JSESSIONID) LoginService returns 500 │
│ │
│ OAuth 2.0 / SMART FHIR ───► 404 — not deployed on Kai │
│ │
│ 2-Legged OAuth ────────────► Rejected (preAuthorizedToken │
│ always null in OSCAR code) │
│ │
│ Basic Auth ────────────────► 401 — not supported │
│ Bearer Token ──────────────► 401 — ignored by OSCAR │
│ API Key Header ────────────► 401 — not recognized │
│ Negotiate/NTLM ───────────► 401 — not supported │
│ Direct DB Token Insert ────► No MySQL access available │
│ │
│ ════════════════════════════════════════════════════════ │
│ OAuth 1.0a 3-Legged REST ──► WORKS │
│ (HMAC-SHA1 signed JSON) JSON passes CF WAF │
│ (/ws/services/*) The Cortico model │
│ ════════════════════════════════════════════════════════ │
│ │
│ Result: OAuth 1.0a REST is the ONLY viable auth path │
│ for Kai-hosted OSCAR behind Cloudflare. │
│ Prerequisite: ModuleNames=REST in oscar.properties │
└──────────────────────────────────────────────────────────────┘
The Cortico Parallel¶
Cortico is the largest third-party integration for OSCAR (~560 clinics on Kai). Their architecture validates our approach:
Cortico's Integration Model Our Model (preferRest=true)
───────────────────────── ──────────────────────────
1. Ask clinic to enable 1. Ask clinic to enable
ModuleNames=REST ModuleNames=REST
↓ ↓
2. 3-legged OAuth flow 2. 3-legged OAuth flow
(browser authorize step) (browser authorize step)
↓ ↓
3. All API calls via 3. All API calls via
OAuth REST /ws/services/* OAuth REST /ws/services/*
↓ ↓
4. CF passes JSON traffic 4. CF passes JSON traffic
— no IP allowlist needed — no IP allowlist needed
Cortico never mentions Cloudflare, IP allowlisting, or SOAP because they don't use any of those. Neither will we.
Decision Flow Per Method Call¶
┌────────────────────────┐
│ adapter.someMethod() │
└───────────┬────────────┘
▼
┌──────────────────────────────────┐
│ this.preferRest │
│ && this.oauthClient != null ? │
└──────┬───────────────┬───────────┘
│ YES │ NO
▼ ▼
┌──────────────┐ ┌─────────────────┐
│ oauthRestCall │ │ SOAP path │
│ (helper) │ │ (existing code │
│ │ │ 100% unchanged)│
└──────┬───────┘ └────────┬────────┘
│ │
┌────┴────┐ return result
SUCCESS FAIL
│ │
▼ ▼
return return REST error
data (NO SOAP fallback)
No REST-to-SOAP Fallback
If CF blocks SOAP, falling back to SOAP after a REST failure just adds latency before the same 403. The preferRest flag is a clean switch — REST or SOAP, not both.
Circuit Breaker Architecture¶
┌─────────────────────────────────────────────────────────────────┐
│ Circuit Breakers │
│ │
│ SOAP breakers (existing, unchanged): │
│ scheduleBreaker ──► SOAP ScheduleService calls │
│ demographicBreaker──► SOAP DemographicService calls │
│ providerBreaker ──► SOAP ProviderService calls │
│ │
│ REST breaker (NEW, separate): │
│ restBreaker ──► ALL OAuth REST calls via oauthRestCall│
│ │
│ Config: 4s timeout, 50% error threshold, 30s reset │
└─────────────────────────────────────────────────────────────────┘
The REST breaker is separate from SOAP breakers to prevent cross-contamination: if REST fails and trips a SOAP breaker, both paths die simultaneously. Separate breakers isolate failure domains.
Implementation Impact¶
What Changes¶
| File | Changes |
|---|---|
OscarSoapAdapter.ts |
+preferRest field, +restBreaker, +oauthRestCall() helper (~40 lines), +REST branch in 8 methods (~15-25 each), updated warmUp() and healthCheck() |
EmrAdapterFactory.ts |
Read oscarPreferRest from DB, pass to adapter |
schema.prisma |
+oscarPreferRest Boolean @default(false) |
What Doesn't Change¶
| File | Reason |
|---|---|
IEmrAdapter.ts |
Interface unchanged |
OscarBridgeAdapter.ts |
Unrelated adapter |
oscar-oauth.ts |
OAuth flow unchanged |
| All SOAP code paths | Zero changes when preferRest=false |
| All existing tests | Zero changes |
Total: ~585 lines added, ~17 modified, 0 deleted.
Risk Register¶
| Severity | Risk | Status | Mitigation |
|---|---|---|---|
| P0 | REST POST blocked by CF | :white_check_mark: PASSED | 8-test matrix proves content-inspection |
| P0 | REST response format unknown | Pending | Capture fixtures from dev OSCAR before coding |
| P0 | ModuleNames=REST is third-party dep |
Pending | Send email to Kai (identical to Cortico onboarding) |
| P1 | /ws/services/ vs /ws/rs/ endpoint differences |
Capture WADL after Kai enables REST | |
| P1 | OAuth token silent expiration | Daily health check + alert | |
| P1 | Dev vs Kai response format divergence | Capture + diff fixtures from both servers | |
| P2 | preferRest boolean wrong abstraction |
Pragmatic for 1 customer; refactor if third protocol needed |
OSCAR Build Comparison¶
| Property | Dev (OpenOSP) | Kai Pro (FreshBay) |
|---|---|---|
| DemographicService methods | 9 | 14 |
| ScheduleService methods | ~14 | 18 |
| RESTful CXF services | 3 (/rs, /services, /oauth) |
1 (/rs only) |
| SOAP services | 14 | 14 |
ModuleNames=REST |
SET | NOT SET |
| Server timezone | UTC | PST (UTC-8) |
| Cloudflare | None | Yes (YYZ PoP) |
| Custom error pages | No (std Tomcat) | Yes (Kai branded) |
| WS-Security result | Works | 500 HTML error |
Why Not Alternatives?¶
| Alternative | Viable? | Why Not |
|---|---|---|
| CF IP allowlist + keep SOAP | Maybe | Kai controls CF, may refuse. Even if granted, WS-Security returns 500. |
New OscarRestAdapter class |
Yes | Rejected by CTO — duplicates ~400 lines of shared transformers, circuit breakers, template cache. |
Use /ws/rs/ (Surface 2) with cookies |
Risky | Cookie-auth is undocumented for server-to-server. No WADL format equivalence. |
| Session cookie via web login | No | CF blocks /login.do. LoginService returns 500. |
| Direct database access | No | Kai-hosted = no DB access. PHIPA/PIPA prohibit custom components on customer EMRs. |
| Wait for FHIR/OAuth 2.0 | No | 404 on /.well-known/smart-configuration. Not deployed, no timeline. |
Remaining Stop-Gates¶
| # | Gate | Status | Action |
|---|---|---|---|
| 1 | REST POST passes Cloudflare | :white_check_mark: PASSED | 8-test matrix, 2026-02-26 |
| 2 | Capture REST response fixtures from dev OSCAR | :hourglass: PENDING | Write capture-rest-fixtures.ts, run against dev |
| 3 | ModuleNames=REST email sent to Kai |
:hourglass: PENDING | CK to confirm via clinic admin |
Related Documentation¶
- OSCAR SOAP API Reference — Complete 14-service SOAP reference
- Adapter Implementations — OscarSoapAdapter and OscarBridgeAdapter details
- FreshBay KAI-OSCAR Integration — First client deployment plan
- OSCAR Data Architecture — Entity relationships and REST API reference
- Integration Roadmap — Multi-phase EMR integration plan
Analysis based on 26+ curl tests, 9 research agents, 4 browser verifications, and OSCAR source code analysis. Date: 2026-02-26.