Organization API integration guide.
For organizations connecting their own software to Oryen Health. Covers how patients are identified across both systems, what is safe to retry, and how device assignment governs whether readings arrive.
API reference
The full endpoint reference, with request and response shapes, is published as an OpenAPI document you can try requests against: Organization API reference.
Read this guide first. The reference describes each endpoint; this explains how they fit together.
For an organization integrating Oryen Health with its own software.
The OpenAPI reference at /api/docs/partner lists every endpoint and field. This
document covers what the reference cannot: how patients are identified across
the two systems, what is safe to retry, and which failures mean "stop" rather
than "try again".
The model in one paragraph
Oryen holds the monitoring record. You create a patient through this API using your own identifier, attach a device to them, and read back readings and alerts — each one carrying that same identifier. You never store an Oryen UUID, and you never keep a mapping table between your patients and ours. The clinical work (NEWS2 scoring, alert thresholds, reminders, escalation) happens in Oryen.
Access
API access is enabled per organization by Oryen, not self-service. Once enabled, an administrator in your organization creates an API client and receives a key once — it is not retrievable afterwards, only rotatable.
Authenticate with either:
x-oryen-api-key: oh_live_<prefix>_<secret>
Authorization: Bearer oh_live_<prefix>_<secret>
The key determines the organization. There is no organization field in any request body, and sending one is rejected.
Scopes
| Scope | Grants |
|---|---|
patients:read |
List and fetch patients |
readings:read |
Readings, per patient or latest across the caseload |
alerts:read |
Open and historic alerts |
devices:read |
Devices and current assignments |
patients:write |
Create and update patients |
devices:write |
Assign and end device assignments |
Write scopes are never granted by default. Request only what the integration uses: a dashboard that reads should not hold a key that can create patients.
Rate limits
| Limit | |
|---|---|
| All requests | 120 per minute per key |
| Write requests | 30 per minute per key, in addition to the above |
Exceeding either returns 429. Writes count against both, so 30 writes plus 90
reads is the ceiling in a minute. If a migration needs more, talk to us rather
than retrying into the limit.
Identifying patients
Two identifiers matter, and they do different jobs.
externalRef — yours. Whatever your system calls the patient: an MRN, an
EHR key, a local id. Opaque to Oryen; we store it and hand it back. It is unique
within your organization, and it is how you address a patient in every request
after creation.
nhsNumber — theirs. Optional but strongly recommended. Validated on the
check digit, not merely on length, so a transposed digit is rejected rather than
silently naming a different person. Unique within your organization.
You address patients by externalRef throughout:
PATCH /api/v1/organization/patients/EHR-00042
Readings and alerts come back carrying patientExternalRef, so a reading can be
filed without a lookup.
NHS numbers appear on the patients endpoint only, behind
patients:read. They are deliberately not embedded in readings or alerts: a key scoped to readings alone should not receive them.
Enrolling a patient
POST /api/v1/organization/patients
x-oryen-api-key: oh_live_…
Content-Type: application/json
{
"externalRef": "EHR-00042",
"fullName": "Jane Doe",
"dateOfBirth": "1958-04-11",
"gender": "female",
"nhsNumber": "9434765919"
}
| Response | Meaning |
|---|---|
201 |
Created |
200 |
This externalRef already names a patient — the existing one is returned |
400 |
Invalid field, or an unexpected one such as organizationId |
409 |
The NHS number already belongs to a different patient here |
Retries are safe
externalRef is the idempotency key. Sending the same create twice returns the
same patient — the second call answers 200 instead of 201 and creates
nothing. This is deliberate: a request that times out has usually succeeded, and
a blind retry must not produce a second record for one person. Half that
person's readings attaching to each copy is the failure this design exists to
prevent.
So: on a timeout, retry the same request unchanged. Do not generate a new
externalRef.
The 201 versus 200 distinction lets you tell a first sync from a replay
without either being an error.
Attaching a device
Devices are physical and are allocated to your organization by Oryen. List them:
GET /api/v1/organization/devices
Then attach one, naming both sides the way you know them:
POST /api/v1/organization/device-assignments
{
"patientExternalRef": "EHR-00042",
"deviceSerialNumber": "ORY-BP-2041",
"consentConfirmed": true
}
consentConfirmed is required rather than defaulted. You hold the relationship
with the patient, so you assert their consent; monitoring somebody without it
must not be reachable by leaving a field out.
Assignment is what makes readings work. Telemetry from a device with no active assignment cannot be attributed to a patient, is parked as an orphan, and never becomes a reading. If readings are not appearing, check the assignment first.
Ending an assignment:
POST /api/v1/organization/device-assignments/{assignmentId}/end
{ "notes": "Device returned" }
Why an assignment may be refused
400 message |
Cause |
|---|---|
Device already has an active assignment |
End the current one first |
Create an active organization rental before starting patient monitoring |
The device is not yet contracted to you — contact Oryen |
This rental is not available for patient monitoring |
Rental status does not permit monitoring |
Rental is linked to a different patient |
That device is committed elsewhere |
These are commercial and safety gates, not transient errors. Retrying will not clear them.
Reading data back
GET /api/v1/organization/readings/latest
GET /api/v1/organization/patients/{patientRef}/readings
GET /api/v1/organization/alerts
GET /api/v1/organization/device-assignments
Every reading carries patientExternalRef, readingType, measuredAt, and a
detail object holding the measured values for that type.
Prefer webhooks to polling where you can — your administrator configures
endpoints in the Oryen portal, and Oryen will push reading.created and
alert.created rather than making you ask.
Errors
| Status | Meaning | Retry? |
|---|---|---|
400 |
Malformed or invalid request | No — fix the request |
401 |
Key missing, malformed, unknown, or disabled | No — check the key |
403 |
Key lacks the scope, IP not allowlisted, or organization API access disabled | No |
404 |
Not in your organization, or does not exist | No |
409 |
Conflicts with an existing record (e.g. NHS number) | No — reconcile first |
429 |
Rate limited | Yes, after a pause |
5xx |
Server-side | Yes, with backoff |
401 is intentionally vague about why a key failed. The precise reason is
recorded in your organization's API request log, visible in the portal — every
refused request is logged there, including the ones you did not expect to make.
A 404 never distinguishes "belongs to another organization" from "does not
exist". That is deliberate and not a bug to work around.
Withheld records
A patient under restricted processing (UK GDPR Art. 18) is withheld from this
API entirely — their record, readings and alerts. Requests for them return 403
with a message naming the restriction, rather than 404, so you can tell a
withheld record from a deleted one and stop retrying.
Before you go live
- Key stored in a secret manager, never in source control
- IP allowlist set, unless your egress addresses rotate
- Only the scopes you use are granted
- Timeout handling retries the same request rather than minting a new
externalRef -
429handled with backoff -
4xxtreated as terminal, not retried in a loop - NHS number sent where you hold one
- Someone owns the API request log in the portal