Physician Registry API
Centralized physician credentials, licensing, and routing. Single source of truth for both GuideGLP and YourEra platforms.
Overview
The Physician Registry is a standalone microservice that consolidates physician credentials,
state licenses, and prescriber routing rules into a single API. It replaces hardcoded
prescriber data in both the YourEra orchestrator (medication-config.ts) and
GuideGLP's local physician tables.
Base URL
https://api.yourera.com
Endpoints are at /physicians/* and /route/* directly (no prefix).
Key Concepts
- Physicians — Core records with NPI, DEA, contact info, and platform affiliation
- Licenses — Per-state medical licenses with expiration tracking
- Routing — Priority-based rules that determine which physician handles prescriptions for a given platform and state
- Platforms —
yourera,guideglp, orboth
Workflow: YourEra Prescription Approval
// 1. Physician approves prescription for patient in LA
// 2. Prescription Orchestrator queries the registry
GET /route?platform=yourera&state=LA
// 3. Registry returns Ali Nolan (priority 10 for LA)
{
"physician": {
"firstName": "Alison",
"lastName": "Nolan",
"npi": "1982609764",
...
},
"licenseNumber": "APRN.027876",
"priority": 10
}
// 4. Orchestrator submits to Pharmacy Router with Ali's credentials
Workflow: GuideGLP Order
// 1. Physician submits order for patient in TX
// 2. GuideGLP verifies license
GET /physicians/{id}/verify-license?state=TX
// 3. Registry confirms authorization
{
"licensed": true,
"licenseNumber": "TEST-TX-001",
"type": "TEST"
}
// 4. GuideGLP fetches full profile for pharmacy submission
GET /physicians/{id}
Authentication
All endpoints (except /health) require HMAC-SHA256 authentication,
using the same pattern as the Pharmacy Router.
Request Headers
| Header | Description |
|---|---|
X-API-Key |
Your API key (from registry_api_keys table) |
X-Timestamp |
ISO 8601 timestamp of the request (must be within 5 minutes) |
X-Signature |
HMAC-SHA256 hex digest of timestamp + '.' + body |
Signing a Request
import crypto from 'node:crypto';
const timestamp = new Date().toISOString();
const body = JSON.stringify(payload);
const signature = crypto
.createHmac('sha256', API_SECRET)
.update(timestamp + '.' + body)
.digest('hex');
const headers = {
'X-API-Key': API_KEY,
'X-Timestamp': timestamp,
'X-Signature': signature,
'Content-Type': 'application/json',
};
crypto.timingSafeEqual() to prevent timing attacks.
Requests older than 5 minutes are rejected.
List Physicians
Returns all active physicians. Supports filtering by platform, organization, and active status.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
platform |
string optional | Filter by platform: yourera, guideglp. Also returns physicians with both. |
organization |
string optional | Filter by organization (case-insensitive) |
active |
boolean optional | Filter by active status. Defaults to true. |
Response 200
{
"physicians": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "ali@supportivbar.com",
"firstName": "Alison",
"lastName": "Nolan",
"suffix": "FNP-C",
"npi": "1982609764",
"organization": "Biologics",
"platform": "both",
"isActive": true,
...
}
]
}
Get Physician
Returns a physician's full profile including all active licenses.
Response 200
{
"physician": {
"id": "550e8400-...",
"firstName": "Alison",
"lastName": "Nolan",
"suffix": "FNP-C",
"npi": "1982609764",
"deaNumber": null,
"phone": "5042504866",
"addressLine1": "1331 Ochsner Blvd",
"addressCity": "Covington",
"addressState": "LA",
"addressZip": "70433",
"canvasPractitionerId": "de87795cca...",
"organization": "Biologics",
"platform": "both",
"isActive": true
},
"licenses": [
{
"state": "LA",
"licenseNumber": "APRN.027876",
"expirationDate": "2027-01-31",
"licenseType": "APRN"
},
...
]
}
Error 404
{ "error": "Physician not found" }
Create Physician
Creates a new physician record.
Request Body
| Field | Type | Description |
|---|---|---|
firstName | string required | First name (max 100) |
lastName | string required | Last name (max 100) |
npi | string required | National Provider Identifier |
email | string optional | Email address (unique) |
suffix | string optional | Credential suffix (FNP-C, MD, DO, PA) |
deaNumber | string optional | DEA registration number |
phone | string optional | Phone number |
fax | string optional | Fax number |
addressLine1 | string optional | Street address |
addressCity | string optional | City |
addressState | string optional | State (2-letter code) |
addressZip | string optional | ZIP code |
signatureImage | string optional | Base64-encoded signature PNG |
organization | string optional | Organization name |
platform | string optional | guideglp, yourera, or both |
canvasPractitionerId | string optional | Canvas EMR practitioner UUID |
Response 201
{
"physician": { /* full physician object */ }
}
Error 409
{ "error": "A physician with this email already exists" }
Update Physician
Updates a physician's profile. All fields are optional — only provided fields are updated.
Request Body
Same fields as Create Physician, all optional.
Response 200
{
"physician": { /* updated physician object */ }
}
Delete Physician
Soft-deletes a physician by setting isActive = false. The record is preserved for audit purposes.
Response 200
{
"success": true,
"physician": { /* physician with isActive: false */ }
}
List Licenses
Returns all active licenses for a physician.
Response 200
{
"licenses": [
{
"id": "...",
"physicianId": "...",
"state": "LA",
"licenseNumber": "APRN.027876",
"expirationDate": "2027-01-31",
"licenseType": "APRN",
"isActive": true
}
]
}
Add License
Adds a new state license to a physician.
Request Body
| Field | Type | Description |
|---|---|---|
state | string required | 2-letter state code (auto-uppercased) |
licenseNumber | string required | License number |
expirationDate | string optional | Expiration date (YYYY-MM-DD) |
licenseType | string optional | License type (MD, DO, NP, FNP, PA, APRN) |
Response 201
{
"license": { /* created license object */ }
}
Update License
Updates a license record. All fields are optional.
Response 200
{
"license": { /* updated license object */ }
}
Delete License
Soft-deletes a license by setting isActive = false.
Response 200
{
"success": true,
"license": { /* license with isActive: false */ }
}
Verify License
Checks whether a physician has an active, non-expired license in the specified state. Used by GuideGLP before submitting orders.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
state |
string required | 2-letter state code (case-insensitive) |
Response: Licensed 200
{
"licensed": true,
"state": "LA",
"licenseNumber": "APRN.027876",
"expirationDate": "2027-01-31",
"type": "APRN"
}
Response: Not Licensed 200
{
"licensed": false,
"state": "TX"
}
Response: Expired 200
{
"licensed": false,
"state": "NM",
"reason": "License expired",
"expirationDate": "2024-01-01"
}
Route Physician
Returns the best physician for a given platform and state based on priority routing rules. The highest-priority physician with an active license wins.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
platform |
string required | yourera or guideglp |
state |
string required | 2-letter state code |
physicianId |
string optional | If provided, verifies this specific physician instead of finding the best one |
Response: Best Physician Found 200
{
"physician": {
"id": "550e8400-...",
"firstName": "Alison",
"lastName": "Nolan",
"suffix": "FNP-C",
"npi": "1982609764",
"deaNumber": null,
"phone": "5042504866",
"fax": null,
"addressLine1": "1331 Ochsner Blvd",
"addressCity": "Covington",
"addressState": "LA",
"addressZip": "70433",
"canvasPractitionerId": "de87795cca..."
},
"licenseNumber": "APRN.027876",
"priority": 10
}
Response: No Physician Available 200
{
"physician": null,
"reason": "No physician available for yourera in ZZ"
}
Response: Specific Physician Verification 200
When physicianId is provided:
// Authorized
{
"authorized": true,
"licenseNumber": "APRN.027876"
}
// Not authorized
{
"authorized": false,
"reason": "No active license in TX"
}
Self-Service Endpoints
These endpoints allow physicians to manage their own profiles, scoped by their API key. Used by the GuideGLP settings page.
Returns the physician's own profile and licenses, based on the API key's associated physician.
Response 200
{
"physician": { ... },
"licenses": [ ... ]
}
Updates the physician's own profile.
Uploads a signature image (base64 PNG).
Request Body
{
"signatureImage": "data:image/png;base64,iVBOR..."
}
Response 200
{ "success": true }
Routing Logic
The routing engine uses a priority-based system. When a caller requests a physician for a platform and state, the registry:
- Finds all active routing rules matching the platform + state
- Sorts by
prioritydescending (highest first) - For each candidate, verifies the physician is active and has a non-expired license in that state
- Returns the first match
Current Routing Rules
| Physician | Platform | States | Priority |
|---|---|---|---|
| Ali Nolan (FNP-C) | yourera |
AK, CT, HI, LA, NM, NY | 10 (preferred) |
| Neelima Singh (MD) | yourera |
All 50 states | 5 (fallback) |
platform=yourera&state=LA returns Ali Nolan (priority 10),
since she outranks Neelima Singh (priority 5) for LA.
A request for state=OH returns Neelima Singh, since Ali has no routing rule for OH.
Adding a New Physician
To add a new physician to the routing:
- Create the physician:
POST /physicians - Add state licenses:
POST /physicians/:id/licenses(one per state) - Insert routing rules into the
physician_routingtable with the desired priority
medication-config.ts
and redeploying, the registry is entirely data-driven. New physicians are added via API calls.
Health Check
Returns service status. No authentication required.
Response 200
{
"status": "ok",
"service": "physician-registry",
"timestamp": "2026-03-20T12:00:00.000Z"
}