OpesCare SDK
The OpesCare SDK provides typed wrappers around the Connect API and FHIR R4 layer. Install via Composer (PHP), npm (TypeScript/JavaScript), or pip (Python) and start making calls in minutes. All three SDKs expose identical module names and method signatures.
Installation
composer require opescare/sdk
npm install @opescare/sdk # or yarn add @opescare/sdk
pip install opescare-sdk
Initialisation
Pass your client_id and client_secret from the developer portal.
The SDK fetches a Bearer token automatically and refreshes it 60 seconds before expiry.
use OpesCare\OpesCareClient;
$client = new OpesCareClient(
clientId: env('OPESCARE_CLIENT_ID'),
clientSecret: env('OPESCARE_CLIENT_SECRET'),
environment: 'sandbox', // or 'production'
);
import { OpesCareClient } from '@opescare/sdk';
// create() is async — it fetches the initial Bearer token
const client = await OpesCareClient.create({
clientId: process.env.OPESCARE_CLIENT_ID,
clientSecret: process.env.OPESCARE_CLIENT_SECRET,
environment: 'sandbox', // or 'production'
});
from opescare import OpesCareClient
import os
client = OpesCareClient(
client_id=os.environ['OPESCARE_CLIENT_ID'],
client_secret=os.environ['OPESCARE_CLIENT_SECRET'],
environment='sandbox', # or 'production'
)
Health ID Methods
client.healthIds.resolve(...)
Resolve a patient to their canonical Health ID. Pass health_id for a direct lookup, or first_name + last_name + date_of_birth for demographic resolution.
// By Health ID
$result = $client->healthIds->resolve(['health_id' => 'CM-HID-7KQ9-MP42-X8D1']);
echo $result['health_id']; // "CM-HID-7KQ9-MP42-X8D1"
echo $result['status']; // "found"
// By demographics
$result = $client->healthIds->resolve([
'first_name' => 'Amara',
'last_name' => 'Ngo',
'date_of_birth' => '1990-04-15',
]);
// By Health ID
const result = await client.healthIds.resolve({ health_id: 'CM-HID-7KQ9-MP42-X8D1' });
console.log(result.health_id); // "CM-HID-7KQ9-MP42-X8D1"
console.log(result.status); // "found"
// By demographics
const result2 = await client.healthIds.resolve({
first_name: 'Amara',
last_name: 'Ngo',
date_of_birth: '1990-04-15',
});
# By Health ID
result = client.health_ids.resolve(health_id='CM-HID-7KQ9-MP42-X8D1')
print(result['health_id']) # "CM-HID-7KQ9-MP42-X8D1"
print(result['status']) # "found"
# By demographics
result2 = client.health_ids.resolve(
first_name='Amara',
last_name='Ngo',
date_of_birth='1990-04-15',
)
result['key'], not result.key.Patient Methods
client.patients.getSummary(healthId)
Returns a consented patient summary — demographics, active allergies, current medications, recent labs, recent visits. Requires patients:read consent scope.
$summary = $client->patients->getSummary('CM-HID-7KQ9-MP42-X8D1');
echo $summary['patient']['first_name']; // "Amara"
echo $summary['patient']['blood_group']; // "O+"
print_r($summary['active_medications']);
print_r($summary['active_allergies']);
const summary = await client.patients.getSummary('CM-HID-7KQ9-MP42-X8D1');
console.log(summary.patient.first_name); // "Amara"
console.log(summary.patient.blood_group); // "O+"
console.log(summary.active_medications);
console.log(summary.active_allergies);
summary = client.patients.get_summary('CM-HID-7KQ9-MP42-X8D1')
print(summary['patient']['first_name']) # "Amara"
print(summary['patient']['blood_group']) # "O+"
print(summary['active_medications'])
print(summary['active_allergies'])
FHIR R4 Methods
Read structured clinical data using FHIR R4 resources. All responses are FHIR R4-compliant JSON bundles.
$hid = 'CM-HID-7KQ9-MP42-X8D1'; // Active medications $meds = $client->fhir->medicationRequests($hid, ['status' => 'active']); // Allergies $allergies = $client->fhir->allergyIntolerances($hid); // Lab results $labs = $client->fhir->diagnosticReports($hid); // Active diagnoses $conditions = $client->fhir->conditions($hid, ['clinical-status' => 'active']); // Full patient bundle (everything) $bundle = $client->fhir->patientEverything($hid);
const hid = 'CM-HID-7KQ9-MP42-X8D1';
const meds = await client.fhir.medicationRequests(hid, { status: 'active' });
const allergies = await client.fhir.allergyIntolerances(hid);
const labs = await client.fhir.diagnosticReports(hid);
const conditions = await client.fhir.conditions(hid, { 'clinical-status': 'active' });
const bundle = await client.fhir.patientEverything(hid);
hid = 'CM-HID-7KQ9-MP42-X8D1'
meds = client.fhir.medication_requests(hid, status='active')
allergies = client.fhir.allergy_intolerances(hid)
labs = client.fhir.diagnostic_reports(hid)
conditions = client.fhir.conditions(hid, **{'clinical-status': 'active'})
bundle = client.fhir.patient_everything(hid)
Push Clinical Records
Push CDSS recommendations, lab interpretations, and prescription alerts back to OpesCare. Idempotency keys are auto-generated — you do not need to manage them manually.
// Push a CDSS clinical recommendation
$result = $client->records->pushEncounter([
'health_id' => 'CM-HID-7KQ9-MP42-X8D1',
'encounter_type' => 'cdss_alert',
'clinical_note' => 'Drug interaction detected: Warfarin + Aspirin — increased bleeding risk.',
'severity' => 'high',
'alert_type' => 'drug_interaction',
'cdss_rule_id' => 'DDI-WARFARIN-ASPIRIN-001',
]);
// Push a lab interpretation
$client->records->pushLabResult([
'health_id' => 'CM-HID-7KQ9-MP42-X8D1',
'test_name' => 'HbA1c',
'result_value' => '9.2%',
'flagged' => true,
'flag_level' => 'critical',
'interpretation' => 'Poor glycemic control — consider treatment escalation.',
]);
// Push a CDSS clinical recommendation
const result = await client.records.pushEncounter({
health_id: 'CM-HID-7KQ9-MP42-X8D1',
encounter_type: 'cdss_alert',
clinical_note: 'Drug interaction detected: Warfarin + Aspirin — increased bleeding risk.',
severity: 'high',
alert_type: 'drug_interaction',
cdss_rule_id: 'DDI-WARFARIN-ASPIRIN-001',
});
// Push a lab interpretation
await client.records.pushLabResult({
health_id: 'CM-HID-7KQ9-MP42-X8D1',
test_name: 'HbA1c',
result_value: '9.2%',
flagged: true,
flag_level: 'critical',
interpretation: 'Poor glycemic control — consider treatment escalation.',
});
# Push a CDSS clinical recommendation
result = client.records.push_encounter(
health_id='CM-HID-7KQ9-MP42-X8D1',
encounter_type='cdss_alert',
clinical_note='Drug interaction detected: Warfarin + Aspirin — increased bleeding risk.',
severity='high',
alert_type='drug_interaction',
cdss_rule_id='DDI-WARFARIN-ASPIRIN-001',
)
# Push a lab interpretation
client.records.push_lab_result(
health_id='CM-HID-7KQ9-MP42-X8D1',
test_name='HbA1c',
result_value='9.2%',
flagged=True,
flag_level='critical',
interpretation='Poor glycemic control — consider treatment escalation.',
)
Webhooks via SDK
Subscribe
$subscription = $client->webhooks->subscribe(
callbackUrl: 'https://your-system.example.com/opescare/webhook',
events: ['lab_result.released', 'prescription.issued', 'consent.revoked'],
description: 'My CDSS event listener',
);
// Save $subscription['webhook_secret'] immediately — shown only once
echo $subscription['webhook_secret']; // "whsec_xxxxxxxxxxxxxxxx"
const subscription = await client.webhooks.subscribe( 'https://your-system.example.com/opescare/webhook', ['lab_result.released', 'prescription.issued', 'consent.revoked'], 'My CDSS event listener' ); // Save subscription.webhook_secret immediately — shown only once console.log(subscription.webhook_secret); // "whsec_xxxxxxxxxxxxxxxx"
subscription = client.webhooks.subscribe(
callback_url='https://your-system.example.com/opescare/webhook',
events=['lab_result.released', 'prescription.issued', 'consent.revoked'],
description='My CDSS event listener',
)
# Save subscription['webhook_secret'] immediately — shown only once
print(subscription['webhook_secret']) # "whsec_xxxxxxxxxxxxxxxx"
Verify Incoming Webhook Signature
use OpesCare\Exceptions\WebhookSignatureException;
Route::post('/opescare/webhook', function (\Illuminate\Http\Request $request) {
try {
$client->webhooks->verifySignature(
$request->getContent(),
$request->header('X-OpesCare-Signature'),
env('OPESCARE_WEBHOOK_SECRET') // "whsec_xxxxxxxxxxxxxxxx"
);
} catch (WebhookSignatureException $e) {
abort(400, 'Invalid webhook signature');
}
$event = $client->webhooks->parseEvent($request->getContent());
// $event['type'] === "lab_result.released"
return response()->json(['received' => true]);
});
import { WebhookSignatureError } from '@opescare/sdk';
import express from 'express';
const app = express();
app.post('/opescare/webhook',
express.raw({ type: 'application/json' }),
async (req, res) => {
try {
client.webhooks.verifySignature(
req.body.toString(),
req.headers['x-opescare-signature'],
process.env.OPESCARE_WEBHOOK_SECRET // "whsec_xxxxxxxxxxxxxxxx"
);
} catch (err) {
if (err instanceof WebhookSignatureError) {
return res.status(400).send('Invalid signature');
}
}
const event = client.webhooks.parseEvent(req.body.toString());
// event.type === "lab_result.released"
res.status(200).json({ received: true });
}
);
from opescare import OpesCareClient
from opescare.exceptions import WebhookSignatureError
from flask import Flask, request, abort, jsonify
app = Flask(__name__)
@app.route('/opescare/webhook', methods=['POST'])
def webhook():
try:
client.webhooks.verify_signature(
raw_payload=request.get_data(),
signature_header=request.headers.get('X-OpesCare-Signature', ''),
secret=os.environ['OPESCARE_WEBHOOK_SECRET'] # "whsec_xxxxxxxxxxxxxxxx"
)
except WebhookSignatureError:
abort(400, 'Invalid signature')
event = client.webhooks.parse_event(request.get_data())
# event['type'] == "lab_result.released"
return jsonify({'received': True})
Error Handling
All exceptions inherit from OpesCareError / OpesCareException.
| Exception | HTTP Status | Cause |
|---|---|---|
AuthenticationError | 401 | Invalid or expired token / credentials |
AuthorizationError | 403 | Token does not have permission for this action |
ConsentRequiredError | 403 | Patient consent not granted |
ValidationError | 422 | Request payload failed server-side validation |
NotFoundException | 404 | Resource does not exist |
IdempotencyConflictError | 409 | Idempotency key reused with different payload |
RateLimitError | 429 | Too many requests — check .retry_after |
ServerError | 5xx | OpesCare server error after all retries exhausted |
WebhookSignatureError | — | HMAC signature invalid, missing, or replay detected |
from opescare import OpesCareClient, AuthenticationError, ConsentRequiredError, RateLimitError
import time
try:
summary = client.patients.get_summary('CM-HID-7KQ9-MP42-X8D1')
except ConsentRequiredError:
# Patient has not granted consent — request it
client.consents.request(
'CM-HID-7KQ9-MP42-X8D1',
purpose='clinical_decision_support',
requested_scopes=['patients:read'],
)
except AuthenticationError:
# Token expired — get a new client
client = client.refresh_token()
except RateLimitError as e:
# Back off for the server-specified duration
time.sleep(e.retry_after)
use OpesCare\Exceptions\{AuthenticationError, ConsentRequiredError, RateLimitError};
try {
$summary = $client->patients->getSummary('CM-HID-7KQ9-MP42-X8D1');
} catch (ConsentRequiredError $e) {
// Request consent
$client->consents->request('CM-HID-7KQ9-MP42-X8D1', 'clinical_decision_support', ['patients:read']);
} catch (AuthenticationError $e) {
// Token expired — create a new client
$client = $client->refreshToken();
} catch (RateLimitError $e) {
sleep($e->retryAfter); // honour server's specified wait time
}
Token Introspection
Check the token's validity, scopes, and expiry via the SDK endpoint:
curl https://opescare.test/api/v1/sdk/token/introspect \
-H "Authorization: Bearer YOUR_BEARER_TOKEN"
# Response:
# {
# "active": true,
# "scopes": ["health_id:verify", "patients:read", "encounters:write"],
# "client_id": "sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
# "expires_at": "2026-06-01T11:00:00Z"
# }