RxRelay Developers
RxRelay API
Submit clinic orders from an external platform through RxRelay. The v1 API supports the clinic's routed medication catalog, patient upsert, required idempotency on writes, and synchronous vendor dispatch once live access is enabled.
Agent-assisted integration
Build with an AI coding agent
Works with Claude Code, Codex, OpenCode, Cursor, and other compatible agents. Copy a prompt into your agent of choice; it installs the skill and starts the workflow.
Start a new integration
Choose Full, Standard, or Minimal clinic-scoped integration.
Install or refresh the latest official RxRelay skill (replaces older copies):
npx skills add https://www.rxrelay.ai/skills/integrate-rxrelay-api/SKILL.md
Then use $integrate-rxrelay-api to implement and verify RxRelay
in this repository. First ask me to choose Full, Standard, or Minimal
clinic-scoped integration, with a concise description of each.
Follow the skill's safety rules and separate optional production hardening.
Finish with the RxRelay readiness reports. Review an existing integration
Find concrete blockers to valid, safe order submission.
Install or refresh the latest official RxRelay skill (replaces older copies):
npx skills add https://www.rxrelay.ai/skills/review-rxrelay-integration/SKILL.md
Then use $review-rxrelay-integration to audit this repository.
Begin read-only and report what is missing or incompatible.
Focus on concrete blockers to valid, safe order submission.
Separate non-blocking hardening recommendations.
Provide the minimum blocker-fix plan and request permission before editing.
Finish with the RxRelay readiness reports. Update to the latest API
Compare your code with recent releases and plan only the required changes.
Install or refresh the latest official RxRelay skill (replaces older copies):
npx skills add https://www.rxrelay.ai/skills/rxrelay-api-update/SKILL.md
Then use $rxrelay-api-update to compare this repository
with the latest RxRelay API.
Report required compatibility and safety changes separately from optional hardening.
Provide the minimum required-change plan and request permission before editing.
After approval, implement, test, and refresh the RxRelay readiness report. Rerun any prompt to refresh its skill; each workflow also checks the canonical published copy before acting. Node 22.20+ required. Secrets stay local; sandbox writes require approval, and live orders are never submitted. Readiness schema
Base URL
https://api.rxrelay.ai/v1
All endpoints are versioned under /v1.
Keys issued from the app start in sandbox mode — submissions are
recorded and returned for review but are not dispatched to the pharmacy until a platform admin enables live access.
Authentication
API keys are clinic-scoped and managed from the RxRelay app under
Settings → API. Pass the key as a bearer token
(or in the X-API-Key header).
Sandbox keys are prefixed rxrelay_sandbox_;
live keys are prefixed rxrelay_live_.
Authorization: Bearer rxrelay_sandbox_your_key Authorizations
Authorization required Bearer rxrelay_sandbox_your_key.
A key with the read_write scope is required to create orders.
Clinic vs. organization keys
Clinic-scoped keys act on a single clinic and are the default.
Organization-scoped keys act on behalf of an organization that owns
many clinics — they must name the target clinic on each order via
clinicId or
externalClinicId, and can
provision clinics and users through the Organizations endpoints
when RxRelay enables it.
OpenAPI
The public partner API specification is available as OpenAPI 3 at
https://api.rxrelay.ai/openapi/v1.json.
This spec only includes upstream partner endpoints under /v1
plus documented webhook payload schemas. Internal RxRelay app and admin endpoints are not included.
Rate limits
The v1 partner API starts at 300 requests per minute per API key. If your clinic needs a higher limit, contact RxRelay and we can raise it after reviewing expected traffic.
429 Retry-After, X-RateLimit-Limit,
X-RateLimit-Remaining, and X-RateLimit-Reset headers.
Errors
Errors use the standard Problem Details JSON shape with a stable code extension and
an X-Request-Id response header. Include the request ID when contacting support.
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Invalid order request",
"detail": "At least one order item is required.",
"status": 400,
"code": "invalid_order_request",
"requestId": "req_2c3d4e5f..."
} Idempotency
POST /v1/orders
requires an Idempotency-Key
header. Keys are scoped to the clinic API key. Retrying the same request with the same key returns the original
order response; reusing the same key with a different body returns 409 Conflict.
Use a stable value from your system, such as partner-order-123.
Going live
New API keys are created in sandbox mode so RxRelay and the clinic can verify the integration before any order is dispatched to the pharmacy network. Use this flow for launch:
- 1. Create a sandbox
read_writekey from Settings → API. - 2. Fetch
GET /v1/medicationsand submit representative sandbox orders with required idempotency keys. - 3. Add a webhook endpoint in Settings → API, copy the one-time endpoint secret, and send a test event to verify signature handling.
- 4. RxRelay reviews the recorded sandbox orders and webhook deliveries for the clinic.
- 5. A platform admin enables live access, which creates a separate
rxrelay_live_key. Copy that key from Settings → API and use it for production order submission.
Sandbox keys remain available for test submissions after live access is enabled. Sandbox orders return synchronously after validation and recording. Live orders return after synchronous vendor dispatch, and configured webhooks receive order status events asynchronously after the API response.
Get clinic
Returns your clinic's record, including its RxRelay id
and whether it is in sandbox mode. The clinic is taken from your API key. Organization keys manage many clinics and
should use GET /v1/clinics instead.
/v1/clinic curl https://api.rxrelay.ai/v1/clinic \
-H "Authorization: Bearer rxrelay_sandbox_your_key" {
"id": "11111111-1111-1111-1111-111111111111",
"name": "Northside Clinic",
"externalClinicId": null,
"address1": "123 Main St",
"address2": null,
"city": "Dallas",
"state": "TX",
"zip": "75201",
"phoneNumber": "5555555555",
"contactEmail": "ops@northsideclinic.com",
"isSandboxMode": true,
"createdAt": "2026-06-19T12:30:00.0000000+00:00"
} Response
id name externalClinicId null for standalone clinics. address1 address2 city state zip phoneNumber contactEmail isSandboxMode createdAt List medications
Returns the authenticated clinic's routed medication catalog, ordered by name. The list is scoped to medications with a configured route through that clinic's pharmacy access; medications outside this list cannot be submitted.
By default the catalog is unified across all pharmacies the clinic can dispense from. To retrieve a single
pharmacy's formulary, pass pharmacyId (an id from List pharmacies).
/v1/medications curl https://api.rxrelay.ai/v1/medications \
-H "Authorization: Bearer rxrelay_sandbox_your_key" curl "https://api.rxrelay.ai/v1/medications?pharmacyId=Kaduceus:opbrx" \
-H "Authorization: Bearer rxrelay_sandbox_your_key" [
{
"id": "7c9e6a1b-1f4d-4a2e-9c3b-9a1d2e3f4a5b",
"name": "Semaglutide 2.5mg/mL",
"strength": "2.5 mg/mL",
"form": "Injectable",
"vialSize": null,
"pharmacyName": "Boothwyn Pharmacy",
"pharmacySku": "SEMA-25-VIAL",
"requiredQuantity": null,
"ingredients": [],
"isControlled": false
},
{
"id": "6f667d6d-5f1d-bf89-546f-29405a4393c7",
"name": "Metoprolol Succinate 25mg ER",
"strength": null,
"form": null,
"vialSize": null,
"pharmacyName": "Boothwyn Pharmacy",
"pharmacySku": "13776",
"requiredQuantity": 6,
"ingredients": [],
"isControlled": false
},
{
"id": "0b8d2f31-6a7c-4d5e-8f90-1a2b3c4d5e6f",
"name": "RENEW",
"strength": null,
"form": "Injectable",
"vialSize": "5mL",
"pharmacyName": "Kaduceus Pharmacy",
"pharmacySku": "11111222219",
"requiredQuantity": null,
"ingredients": [
{ "name": "MOTS-C", "concentration": 2, "unit": "mg/mL" },
{ "name": "BPC-157", "concentration": 2, "unit": "mg/mL" },
{ "name": "GHK-Cu", "concentration": 2, "unit": "mg/mL" },
{ "name": "Kisspeptin-10", "concentration": 10, "unit": "mg/mL" }
],
"isControlled": false
}
] Pharmacy-required quantities
Treat the medication catalog as the source of truth for quantity rules. When a selected medication's
requiredQuantity is non-null, convert that integer to a string and send it as the matching
items[].quantity in POST /v1/orders. When it is null, collect the quantity from the
prescriber as usual. Do not infer the rule from the medication name or SKU, and do not maintain a
partner-side SKU allowlist; pharmacy rules can vary and change independently.
const medication = medications.find(
({ id }) => id === selectedMedicationId,
);
if (!medication) throw new Error("Medication is not available");
const quantity = medication.requiredQuantity == null
? userSelectedQuantity
: String(medication.requiredQuantity);
const orderItem = {
medicationId: medication.id,
quantity,
directions,
refills,
}; Query parameters
pharmacyId id from List pharmacies. Omit for the clinic's full unified catalog. A malformed id returns 400; a well-formed id the clinic isn't entitled to returns an empty array. clinicId externalClinicId. Ignored for clinic-scoped keys. externalClinicId Response
[] Show child attributes
id items[].medicationId when creating an order. name strength 2.5 mg/mL), when the pharmacy provides it. form Injectable, Tablet), when available. vialSize 2 mL), when available. Used for injectable vials and other measured packages such as creams; null when the pharmacy does not provide it. pharmacyName pharmacySku requiredQuantity items[].quantity; any other value returns 400 Bad Request. When null, the prescriber may choose the quantity. isControlled ingredients strength alone can't describe it. Not required to order.
Show child attributes
name BPC-157. concentration mg units). unit mg/mL. List pharmacies
Returns the distinct pharmacies the authenticated clinic can dispense from. Each carries a stable, opaque
id you pass as the pharmacyId filter on
List medications to retrieve that pharmacy's
formulary. Use this when you route across multiple pharmacies and need per-pharmacy catalogs.
/v1/pharmacies curl https://api.rxrelay.ai/v1/pharmacies \
-H "Authorization: Bearer rxrelay_sandbox_your_key" [
{
"id": "Boothwyn",
"name": "Boothwyn Pharmacy",
"platform": "Boothwyn"
},
{
"id": "Kaduceus:opbrx",
"name": "Kaduceus Pharmacy",
"platform": "Kaduceus"
}
] Response
[] Show child attributes
id pharmacyId on List medications. name platform List prescribers
Returns prescribers in the clinic that have an NPI on file, ordered by name.
/v1/prescribers curl https://api.rxrelay.ai/v1/prescribers \
-H "Authorization: Bearer rxrelay_sandbox_your_key" [
{
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"firstName": "Alex",
"lastName": "Reed",
"email": "alex.reed@northsideclinic.com",
"npi": "1234567890",
"licenseState": "TX",
"licenseNumber": "MD-44821"
}
] Response
[] Show child attributes
userId prescriberUserId when creating an order. firstName lastName email npi prescriberNpi when creating an order. licenseState licenseNumber Register a user
Creates or updates a user in your clinic and assigns clinic roles — use it to register prescribers programmatically
instead of the onboarding spreadsheet. The clinic is taken from your API key, so no clinic reference is needed. A
prescriber must include an
npi, after which it can be
referenced on orders by prescriberNpi.
Idempotent per email: an existing email links to that user and merges roles rather than creating a duplicate. Requires a
read_write key.
/v1/users curl -X POST https://api.rxrelay.ai/v1/users \
-H "Authorization: Bearer rxrelay_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"email": "alex.reed@northsideclinic.com",
"firstName": "Alex",
"lastName": "Reed",
"roles": ["prescriber"],
"npi": "1234567890",
"licenseState": "TX",
"licenseNumber": "MD-44821"
}' {
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"email": "alex.reed@northsideclinic.com",
"firstName": "Alex",
"lastName": "Reed",
"roles": ["prescriber"],
"npi": "1234567890",
"joinedAt": "2026-06-19T12:31:00.0000000+00:00"
} Body
email required firstName lastName roles required prescriber, clinic_admin, staff, or owner. platform_admin cannot be assigned via the API. npi roles includes prescriber. licenseState licenseNumber phone deaNumber Response
userId email firstName lastName roles npi joinedAt List users
Returns every user in your clinic with their roles and NPI. Use it to check whether a prescriber's NPI is already registered before submitting an order. The clinic is taken from your API key.
/v1/users curl https://api.rxrelay.ai/v1/users \
-H "Authorization: Bearer rxrelay_sandbox_your_key" [
{
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"email": "alex.reed@northsideclinic.com",
"firstName": "Alex",
"lastName": "Reed",
"roles": ["prescriber"],
"npi": "1234567890",
"joinedAt": "2026-06-19T12:31:00.0000000+00:00"
}
] Response
[] Show child attributes
userId email firstName lastName roles npi joinedAt List patients
Lists or searches patients in the clinic, ordered by name. search
is a case-insensitive substring match over name, email, and phone, plus an exact match on date of birth.
email and
externalPatientId are exact-match filters.
Results are paginated with limit and
offset.
/v1/patients curl "https://api.rxrelay.ai/v1/patients?search=jane&limit=25&offset=0" \
-H "Authorization: Bearer rxrelay_sandbox_your_key" {
"patients": [
{
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"externalPatientId": "patient-456",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1980-01-10",
"sex": "F",
"phone": "5555555555",
"email": "jane@example.com",
"createdAt": "2026-06-05T12:15:00.0000000+00:00",
"addresses": [
{
"id": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true,
"isActive": true,
"createdAt": "2026-06-05T12:15:00.0000000+00:00"
}
]
}
],
"total": 1,
"limit": 25,
"offset": 0
} Query parameters
clinicId externalClinicId. Ignored for clinic-scoped keys. externalClinicId search 1980-01-10). email externalPatientId limit offset Response
patients Show child attributes
patientId externalPatientId firstName lastName dateOfBirth 1980-01-10. sex M, F, or U. phone email createdAt addresses Patient addresses. total limit offset Get a patient
Fetches a single patient by RxRelay ID. Returns 404 Not Found if the patient does not belong to your clinic.
/v1/patients/{patientId} curl https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e \
-H "Authorization: Bearer rxrelay_sandbox_your_key" {
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"externalPatientId": "patient-456",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1980-01-10",
"sex": "F",
"phone": "5555555555",
"email": "jane@example.com",
"createdAt": "2026-06-05T12:15:00.0000000+00:00",
"addresses": [
{
"id": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true,
"isActive": true,
"createdAt": "2026-06-05T12:15:00.0000000+00:00"
}
]
} Path parameters
patientId required
Returns the patient and its active addresses (default first). Organization-scoped keys may pass
clinicId or
externalClinicId as a query parameter.
Create a patient
Resolves or creates a patient outside of an order, using the same matching as order creation: first by
externalPatientId, then by normalized email;
a new patient is created when none match. firstName,
lastName,
dateOfBirth,
email, and
phone are required to create — email and phone are required to prescribe. Requires a
read_write key.
/v1/patients curl -X POST https://api.rxrelay.ai/v1/patients \
-H "Authorization: Bearer rxrelay_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"externalPatientId": "patient-456",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1980-01-10",
"sex": "F",
"phone": "5555555555",
"email": "jane@example.com",
"defaultAddress": {
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true
}
}' {
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"externalPatientId": "patient-456",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1980-01-10",
"sex": "F",
"phone": "5555555555",
"email": "jane@example.com",
"createdAt": "2026-06-05T12:15:00.0000000+00:00",
"addresses": [
{
"id": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true,
"isActive": true,
"createdAt": "2026-06-05T12:15:00.0000000+00:00"
}
]
} {
"externalPatientId": "patient-456",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1980-01-10",
"sex": "F",
"phone": "5555555555",
"email": "jane@example.com",
"defaultAddress": {
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true
}
} Body
externalPatientId firstName lastName dateOfBirth 1980-01-10. Required when creating a new patient. sex M, F, or U. phone email externalPatientId is absent. defaultAddress Show child attributes
address1 required address2 city required state required TX. zip required isDefault Update a patient
Updates a patient's demographics. Supplied (non-null) fields overwrite; omitted or null fields are left unchanged.
Setting externalPatientId (re)maps your identifier
to this patient and returns 409 Conflict if it is already
mapped to a different patient in the clinic. Requires a read_write key.
/v1/patients/{patientId} curl -X PATCH https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e \
-H "Authorization: Bearer rxrelay_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"phone": "5125550199",
"email": "jane.doe@example.com",
"externalPatientId": "patient-456"
}' {
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"externalPatientId": "patient-456",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1980-01-10",
"sex": "F",
"phone": "5555555555",
"email": "jane@example.com",
"createdAt": "2026-06-05T12:15:00.0000000+00:00",
"addresses": [
{
"id": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true,
"isActive": true,
"createdAt": "2026-06-05T12:15:00.0000000+00:00"
}
]
} {
"phone": "5125550199",
"email": "jane.doe@example.com",
"externalPatientId": "patient-456"
} Body
firstName lastName dateOfBirth sex M, F, or U. phone email externalPatientId 409 Conflict if already mapped to a different patient. Create an order
Submits an order with one or more Rx items. The patient is resolved in order of precedence: first by
patientId
(a direct match to an existing RxRelay patient — when set, all other patient fields are ignored), then by
externalPatientId, then by normalized email; a new patient is created when none match. Every item must reference a
medication from GET /v1/medications.
/v1/orders curl -X POST https://api.rxrelay.ai/v1/orders \
-H "Authorization: Bearer rxrelay_sandbox_your_key" \
-H "Idempotency-Key: partner-order-123" \
-H "Content-Type: application/json" \
-d '{
"externalOrderId": "partner-order-123",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"patient": {
"externalPatientId": "patient-456",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1980-01-10",
"sex": "F",
"email": "jane@example.com",
"phone": "5555555555"
},
"shippingAddress": {
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201"
},
"prescriberNpi": "1234567890",
"shippingMethod": "Standard",
"items": [
{
"externalPrescriptionId": "rx-001",
"medicationId": "7c9e6a1b-1f4d-4a2e-9c3b-9a1d2e3f4a5b",
"quantity": "30",
"directions": "Take one capsule daily",
"pharmacyNotes": "Clinical Difference Statement: Patient requires compounded formulation due to clinical need.",
"refills": 2
}
]
}' {
"orderId": "a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d",
"externalOrderId": "partner-order-123",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"prescriber": {
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"firstName": "Alex",
"lastName": "Reed",
"npi": "1234567890"
},
"status": "SandboxReceived",
"shippingMethod": "Standard",
"trackingNumber": null,
"createdAt": "2026-06-05T12:30:00.0000000+00:00",
"updatedAt": "2026-06-05T12:30:00.0000000+00:00",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"items": [
{
"orderItemId": "c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f",
"medicationId": "7c9e6a1b-1f4d-4a2e-9c3b-9a1d2e3f4a5b",
"medicationName": "Semaglutide 2.5mg/mL",
"directions": "Take one capsule daily",
"externalPrescriptionId": "rx-001",
"pharmacyNotes": "Clinical Difference Statement: Patient requires compounded formulation due to clinical need.",
"isSupply": false,
"status": "SandboxReceived"
}
],
"submissions": [
{
"submissionId": "d3e4f5a6-b7c8-4d9e-0f1a-2b3c4d5e6f70",
"environment": "Sandbox",
"status": "SandboxReceived",
"orderItemIds": ["c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f"],
"failureReason": null
}
],
"approval": null
} {
"externalOrderId": "partner-order-124",
"patient": {
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e"
},
"shippingAddressId": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"prescriberNpi": "1234567890",
"shippingMethod": "Standard",
"items": [
{
"externalPrescriptionId": "rx-002",
"medicationId": "7c9e6a1b-1f4d-4a2e-9c3b-9a1d2e3f4a5b",
"quantity": "30",
"directions": "Take one capsule daily",
"refills": 2
}
]
} Headers
Authorization required read_write API key. Idempotency-Key required 409 Conflict.
Body
clinicId externalClinicId. Ignored for clinic-scoped keys. externalClinicId clinicId. externalOrderId metadata patient required Show child attributes
patientId externalPatientId firstName lastName dateOfBirth 1980-01-10. Required when creating a new patient. sex M, F, or U. phone email externalPatientId is absent. shippingAddress shippingAddress or shippingAddressId. A supplied address is stored on the patient record, matched against any identical existing address, made default, and sent to the fulfilling pharmacy.
Show child attributes
address1 required address2 city required state required TX. zip required shippingAddressId shippingAddressId or shippingAddress. The address must belong to the resolved patient and be active.
prescriberUserId prescriberNpi; the prescriber must belong to the clinic.
prescriberNpi prescriberUserId is omitted. shippingMethod items required Show child attributes
medicationId required GET /v1/medications. medicationName quantity required 30. When the selected medication has a non-null requiredQuantity, this value must match it or the order returns 400 Bad Request. directions required pharmacyNotes notes field and must also total 500 characters or fewer; supplying this field for another pharmacy returns 400 Bad Request. refills required externalPrescriptionId Response
orderId externalOrderId externalOrderId you supplied, if any. patientId prescriber prescriberUserId or prescriberNpi you supplied. Null when the order has no prescriber on record.
Show child attributes
userId userId returned by List prescribers. firstName lastName npi status Queued, SandboxReceived, DemoRecorded, Pending, Sent, Shipped, Delivered, Failed, Cancelled, or PendingApproval.
PendingApproval means the order was staged inside the RxRelay app and is waiting on a prescriber — nothing has been sent to a pharmacy. Orders created through this API are never in that state.
shippingMethod Standard or Overnight. It may differ from the requested value when the pharmacy owns service selection; LifeFile is always Overnight, while Boothwyn's vendor-native service is configured separately and represented here as Standard. trackingNumber createdAt updatedAt createdAt until the order changes. metadata items Show child attributes
orderItemId medicationId medicationName directions externalPrescriptionId pharmacyNotes isSupply status Queued before dispatch. submissions Show child attributes
submissionId environment Sandbox or Production. status orderItemIds failureReason approval null for orders created through this API.
Show child attributes
status Pending, Approved, or Rejected. submittedForApprovalAt reviewedAt note Errors
400 Bad Request
Missing Idempotency-Key, a missing or invalid shipping address, a patient missing a required email or phone, an unknown or unsupported medication, no resolvable prescriber, a prescriber with no NPI on file, an item whose quantity does not match the medication catalog's requiredQuantity, or invalid pharmacyNotes for the selected pharmacy or length limit.
402 Payment Required
The clinic has a past-due balance. Settle billing before submitting live orders.
403 Forbidden
The API key is read-only. Use a key with the read_write scope to create orders.
404 Not Found
A referenced patient or resource does not belong to this clinic.
409 Conflict
The Idempotency-Key was reused with a different request body, or the original request is still processing.
429 Too Many Requests
The API key exceeded its current rate limit. Retry after the Retry-After header or contact RxRelay to raise clinic limits.
Retrieve an order
Fetches the current status of an order and its items by RxRelay order ID.
/v1/orders/{orderId} curl https://api.rxrelay.ai/v1/orders/a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d \
-H "Authorization: Bearer rxrelay_sandbox_your_key" {
"orderId": "a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d",
"externalOrderId": "partner-order-123",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"prescriber": {
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"firstName": "Alex",
"lastName": "Reed",
"npi": "1234567890"
},
"status": "Shipped",
"shippingMethod": "Standard",
"trackingNumber": "1Z999AA10123456784",
"createdAt": "2026-06-05T12:30:00.0000000+00:00",
"updatedAt": "2026-06-08T16:45:12.0000000+00:00",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"items": [
{
"orderItemId": "c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f",
"medicationId": "7c9e6a1b-1f4d-4a2e-9c3b-9a1d2e3f4a5b",
"medicationName": "Semaglutide 2.5mg/mL",
"directions": "Take one capsule daily",
"externalPrescriptionId": "rx-001",
"pharmacyNotes": "Clinical Difference Statement: Patient requires compounded formulation due to clinical need.",
"isSupply": false,
"status": "Shipped"
}
],
"submissions": [
{
"submissionId": "d3e4f5a6-b7c8-4d9e-0f1a-2b3c4d5e6f70",
"environment": "Production",
"status": "Shipped",
"orderItemIds": ["c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f"],
"failureReason": null
}
],
"approval": null
} Path parameters
orderId required
Returns the same shape as Create an order,
or 404 Not Found if the order does not belong to your clinic.
Approve or reject an order
Optional two-step ordering. Send submitForApproval: true
on create to hold an order in the RxRelay approval queue instead of dispatching it, then release it here.
Nothing reaches a pharmacy and nothing is billed until it is approved.
Use this when your own product distinguishes staff who prepare orders from clinicians who sign off on them. You decide who may do what in your app; RxRelay independently verifies that the user you name is authorized here.
/v1/orders/{orderId}/approval curl -X POST https://api.rxrelay.ai/v1/orders \
-H "Authorization: Bearer rxrelay_sandbox_your_key" \
-H "Idempotency-Key: partner-order-125" \
-H "Content-Type: application/json" \
-d '{
"externalOrderId": "partner-order-125",
"submitForApproval": true,
"stagedByUserId": "5c4b3a29-8d7e-4f61-b0a2-3c4d5e6f7a8b",
"patient": {
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e"
},
"shippingAddressId": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"prescriberNpi": "1234567890",
"shippingMethod": "Standard",
"items": [
{
"externalPrescriptionId": "rx-003",
"medicationId": "7c9e6a1b-1f4d-4a2e-9c3b-9a1d2e3f4a5b",
"quantity": "30",
"directions": "Take one capsule daily",
"pharmacyNotes": "Clinical Difference Statement: Patient requires compounded formulation due to clinical need.",
"refills": 2
}
]
}' curl -X POST https://api.rxrelay.ai/v1/orders/a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d/approval \
-H "Authorization: Bearer rxrelay_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"approve": true,
"approverUserId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d"
}' {
"approve": false,
"approverUserId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"note": "Dose needs review before this goes out."
} Body parameters
approve required true releases the order to the pharmacy. false rejects it permanently — a rejected order is never sent and cannot be un-rejected.
approverUserId required prescriber, owner, or clinic_admin.
Use GET /v1/users to look up ids and roles. This user is recorded as the approver on the order.
prescriberUserId note Create-order fields
submitForApproval stagedByUserId Identity
RxRelay validates that approverUserId
is authorized to approve in that clinic, but cannot observe that the person acted — the same trust already placed in the prescriber you name when creating an order.
The originating API key is recorded alongside the approval, so an approval made through this API stays distinguishable from one made in the RxRelay app.
Errors
400 Bad Request
approverUserId is missing, is not a member of the order's clinic, or is not a prescriber, owner, or clinic admin there. Also returned when an overriding prescriberUserId is not a prescriber in the clinic or has no NPI on file.
402 Payment Required
Approving would dispatch the order, but the clinic is past due or has no card on file.
403 Forbidden
The API key is read-only, or ordering is disabled for the clinic.
404 Not Found
No order with that id is reachable by this API key.
409 Conflict
The order was already approved or rejected, was cancelled, or never required approval.
List orders
Lists or searches the clinic's orders, newest first, with pagination. Each summary includes the latest
tracking number and an update timestamp, so you can identify changed orders before fetching full item and
submission detail via GET /v1/orders/{orderId}.
/v1/orders curl "https://api.rxrelay.ai/v1/orders?status=Shipped&limit=25&offset=0" \
-H "Authorization: Bearer rxrelay_sandbox_your_key" {
"orders": [
{
"orderId": "a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d",
"externalOrderId": "partner-order-123",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"prescriber": {
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"firstName": "Alex",
"lastName": "Reed",
"npi": "1234567890"
},
"status": "Shipped",
"shippingMethod": "Standard",
"trackingNumber": "1Z999AA10123456784",
"createdAt": "2026-07-24T16:35:42.5802830+00:00",
"updatedAt": "2026-08-05T14:54:29.0444470+00:00",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"approval": null
}
],
"total": 1,
"limit": 25,
"offset": 0
} Query parameters
clinicId externalClinicId. externalClinicId status Queued, Pending, Sent, Shipped, Delivered, Failed, Cancelled, SandboxReceived, DemoRecorded, or PendingApproval. patientId externalOrderId from to limit offset Response
orders Show child attributes
orderId externalOrderId patientId prescriber userId, firstName, lastName, and npi. Null when the order has no prescriber on record.
status shippingMethod Standard or Overnight. Pharmacy-owned service selection may override the requested value. trackingNumber createdAt updatedAt createdAt until the order changes. metadata approval total limit offset Patient orders
Lists a single patient's orders, newest first, with pagination. Same summary shape and pagination as
List orders. Returns
404 Not Found if the patient does not belong to your clinic.
/v1/patients/{patientId}/orders curl "https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e/orders?limit=25&offset=0" \
-H "Authorization: Bearer rxrelay_sandbox_your_key" {
"orders": [
{
"orderId": "a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d",
"externalOrderId": "partner-order-123",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"prescriber": {
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"firstName": "Alex",
"lastName": "Reed",
"npi": "1234567890"
},
"status": "Shipped",
"shippingMethod": "Standard",
"trackingNumber": "1Z999AA10123456784",
"createdAt": "2026-07-24T16:35:42.5802830+00:00",
"updatedAt": "2026-08-05T14:54:29.0444470+00:00",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"approval": null
}
],
"total": 1,
"limit": 25,
"offset": 0
} Path parameters
patientId required Patient addresses
Retrieve or upsert addresses for an existing RxRelay patient. Upserts return the existing address when
the normalized address already matches, so partners can safely store the returned address ID and later
pass it as shippingAddressId.
/v1/patients/{patientId}/addresses curl https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e/addresses \
-H "Authorization: Bearer rxrelay_sandbox_your_key" [
{
"id": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true,
"isActive": true,
"createdAt": "2026-06-05T12:15:00.0000000+00:00"
}
] Upsert an address
/v1/patients/{patientId}/addresses curl -X POST https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e/addresses \
-H "Authorization: Bearer rxrelay_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true
}' {
"id": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true,
"isActive": true,
"createdAt": "2026-06-05T12:15:00.0000000+00:00"
} Get one address
/v1/patients/{patientId}/addresses/{addressId} curl https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e/addresses/9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210 \
-H "Authorization: Bearer rxrelay_sandbox_your_key" {
"id": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true,
"isActive": true,
"createdAt": "2026-06-05T12:15:00.0000000+00:00"
} Remove an address
Soft-deletes (deactivates) an address. If it was the default, the most recently updated remaining active
address is promoted to default so the patient still has one. Returns the deactivated address. Requires a
read_write key.
/v1/patients/{patientId}/addresses/{addressId} curl -X DELETE https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e/addresses/9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210 \
-H "Authorization: Bearer rxrelay_sandbox_your_key" Set the default address
Promotes an existing active address to the patient's default. An inactive address returns
400 Bad Request — re-add it first. Requires a
read_write key.
/v1/patients/{patientId}/addresses/{addressId}/default curl -X POST https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e/addresses/9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210/default \
-H "Authorization: Bearer rxrelay_sandbox_your_key" {
"id": "9b8a7c6d-5e4f-4321-9a8b-7c6d5e4f3210",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"isDefault": true,
"isActive": true,
"createdAt": "2026-06-05T12:15:00.0000000+00:00"
} Address fields
id shippingAddressId when creating an order. patientId address1 address2 city state zip isDefault isActive false. createdAt
Organization-scoped keys should include clinicId
or externalClinicId
as query parameters, matching the medication and prescriber listing endpoints.
Organizations
An organization is a parent that owns many clinics. A partner managing multiple clinics is issued a single organization-scoped API key instead of one key per clinic. With it you can submit orders for any clinic beneath the org, list those clinics, and — when RxRelay enables provisioning — create new clinics and their users.
Ordering on behalf of a clinic
Use the same POST /v1/orders endpoint,
but include clinicId or
externalClinicId so RxRelay knows which clinic
the order and patient belong to. The named clinic must belong to your organization. A missing identifier returns
400; one that does not match a clinic in your
organization returns 404.
GET /v1/medications,
GET /v1/pharmacies, and
GET /v1/prescribers accept a
clinicId or
externalClinicId query parameter to scope their results.
curl -X POST https://api.rxrelay.ai/v1/orders \
-H "Authorization: Bearer rxrelay_sandbox_your_org_key" \
-H "Idempotency-Key: partner-order-123" \
-H "Content-Type: application/json" \
-d '{
"externalClinicId": "clinic-a",
"externalOrderId": "partner-order-123",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"patient": {
"externalPatientId": "patient-456",
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "1980-01-10",
"sex": "F"
},
"shippingAddress": {
"address1": "123 Main St",
"city": "Dallas",
"state": "TX",
"zip": "75201"
},
"prescriberNpi": "1234567890",
"items": [
{
"medicationId": "7c9e6a1b-1f4d-4a2e-9c3b-9a1d2e3f4a5b",
"quantity": "30",
"directions": "Take one capsule daily",
"refills": 2
}
]
}' List clinics
Returns the clinics under your organization.
/v1/clinics curl https://api.rxrelay.ai/v1/clinics \
-H "Authorization: Bearer rxrelay_sandbox_your_org_key" [
{
"id": "11111111-1111-1111-1111-111111111111",
"name": "Northside Clinic",
"externalClinicId": "clinic-a",
"address1": "123 Main St",
"address2": null,
"city": "Dallas",
"state": "TX",
"zip": "75201",
"phoneNumber": "5555555555",
"contactEmail": "ops@northsideclinic.com",
"isSandboxMode": true,
"createdAt": "2026-06-19T12:30:00.0000000+00:00"
}
] Create a clinic
Creates a clinic under your organization. Requires that RxRelay has enabled clinic provisioning for your organization,
otherwise 403 Forbidden. Supplying
externalClinicId makes the call idempotent —
re-posting the same value returns the existing clinic instead of a duplicate.
/v1/clinics curl -X POST https://api.rxrelay.ai/v1/clinics \
-H "Authorization: Bearer rxrelay_sandbox_your_org_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Northside Clinic",
"externalClinicId": "clinic-a",
"address1": "123 Main St",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"phoneNumber": "5555555555",
"contactEmail": "ops@northsideclinic.com"
}' {
"id": "11111111-1111-1111-1111-111111111111",
"name": "Northside Clinic",
"externalClinicId": "clinic-a",
"address1": "123 Main St",
"address2": null,
"city": "Dallas",
"state": "TX",
"zip": "75201",
"phoneNumber": "5555555555",
"contactEmail": "ops@northsideclinic.com",
"isSandboxMode": true,
"createdAt": "2026-06-19T12:30:00.0000000+00:00"
} Body
name required externalClinicId address1 address2 city state zip phoneNumber contactEmail Create a clinic user
Creates or updates a user in a clinic under your organization and assigns clinic roles.
{clinicRef} is the RxRelay clinic id (a UUID)
of a clinic in your organization. Requires provisioning to be enabled. A
prescriber must include an
npi. Idempotent per email within the clinic.
A single clinic managing its own roster with a clinic-scoped key should use
POST /v1/users instead.
/v1/clinics/{clinicRef}/users curl -X POST https://api.rxrelay.ai/v1/clinics/11111111-1111-1111-1111-111111111111/users \
-H "Authorization: Bearer rxrelay_sandbox_your_org_key" \
-H "Content-Type: application/json" \
-d '{
"email": "alex.reed@northsideclinic.com",
"firstName": "Alex",
"lastName": "Reed",
"roles": ["prescriber"],
"npi": "1234567890",
"licenseState": "TX",
"licenseNumber": "MD-44821"
}' {
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"email": "alex.reed@northsideclinic.com",
"firstName": "Alex",
"lastName": "Reed",
"roles": ["prescriber"],
"npi": "1234567890",
"joinedAt": "2026-06-19T12:31:00.0000000+00:00"
} Body
email required firstName lastName roles required prescriber, clinic_admin, staff, or owner. platform_admin cannot be assigned via the API. npi roles includes prescriber. licenseState licenseNumber phone deaNumber List clinic users
Returns every user in the named clinic with their roles and NPI.
{clinicRef} is the RxRelay clinic id (a UUID)
of a clinic in your organization. The response shape matches
GET /v1/users.
/v1/clinics/{clinicRef}/users [
{
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"email": "alex.reed@northsideclinic.com",
"firstName": "Alex",
"lastName": "Reed",
"roles": ["prescriber"],
"npi": "1234567890",
"joinedAt": "2026-06-19T12:31:00.0000000+00:00"
}
] Webhooks
Configure webhook endpoints from Settings / API to receive order events. RxRelay signs every delivery with a per-endpoint secret and records delivery attempts in the webhook event log for auditability. Outbound webhooks are delivered asynchronously from order submission and pharmacy status processing, so your endpoint response time does not delay RxRelay API responses. RxRelay attempts each delivery once; failed deliveries can be replayed from the event log after your endpoint is healthy again.
Treat webhooks as asynchronous, at-least-once notifications and deduplicate by
RxRelay-Event-Id
or
RxRelay-Delivery-Id.
curl -X POST https://api.rxrelay.ai/clinics/{clinicId}/webhooks/endpoints \
-H "Authorization: Bearer your_app_session_token" \
-H "X-Clinic-Id: {clinicId}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/rxrelay/webhooks",
"environment": "both",
"events": [
"order.sandbox_received",
"order.sent",
"order.failed",
"order.tracking_updated",
"order.pending_approval",
"order.approved",
"order.rejected"
]
}' {
"endpoint": {
"id": "8f76c5b4-3a21-4c90-93a6-7a8b9c0d1e2f",
"clinicId": "11111111-1111-1111-1111-111111111111",
"url": "https://example.com/rxrelay/webhooks",
"secretPrefix": "whsec_4f8b2c91",
"environment": "both",
"events": [
"order.sandbox_received",
"order.sent",
"order.failed",
"order.tracking_updated",
"order.pending_approval",
"order.approved",
"order.rejected"
],
"isActive": true,
"createdAt": "2026-06-05T12:30:00.0000000+00:00",
"updatedAt": "2026-06-05T12:30:00.0000000+00:00",
"secretRotatedAt": "2026-06-05T12:30:00.0000000+00:00"
},
"secret": "whsec_4f8b2c91..."
} Endpoint fields
url required environment sandbox, live, or both. events Delivery payload
RxRelay-Event-Id: evt_f2a4c6e8d0b14d09a7c1e3f5b9a2c4d6
RxRelay-Delivery-Id: 9b8a7c6d-5e4f-3210-9876-abcdef123456
RxRelay-Timestamp: 1780691400
RxRelay-Signature: t=1780691400,v1=<hmac_sha256> {
"id": "evt_f2a4c6e8d0b14d09a7c1e3f5b9a2c4d6",
"type": "order.sandbox_received",
"environment": "sandbox",
"createdAt": "2026-06-05T12:30:00.0000000+00:00",
"data": {
"order": {
"orderId": "a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d",
"externalOrderId": "partner-order-123",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"prescriber": {
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"firstName": "Alex",
"lastName": "Reed",
"npi": "1234567890"
},
"status": "SandboxReceived",
"shippingMethod": "Standard",
"trackingNumber": null,
"createdAt": "2026-06-05T12:30:00.0000000+00:00",
"updatedAt": "2026-06-05T12:30:00.0000000+00:00",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"items": [
{
"orderItemId": "c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f",
"medicationId": "7c9e6a1b-1f4d-4a2e-9c3b-9a1d2e3f4a5b",
"medicationName": "Semaglutide 2.5mg/mL",
"directions": "Take one capsule daily",
"externalPrescriptionId": "rx-001",
"pharmacyNotes": "Clinical Difference Statement: Patient requires compounded formulation due to clinical need.",
"isSupply": false,
"status": "SandboxReceived"
}
],
"submissions": [
{
"submissionId": "d3e4f5a6-b7c8-4d9e-0f1a-2b3c4d5e6f70",
"environment": "Sandbox",
"status": "SandboxReceived",
"orderItemIds": ["c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f"],
"failureReason": null
}
],
"approval": null
}
}
} {
"id": "evt_8d2f8a7e6c5b4a3d9e0f1a2b3c4d5e6f",
"type": "order.tracking_updated",
"environment": "live",
"createdAt": "2026-06-05T13:45:00.0000000+00:00",
"data": {
"order": {
"orderId": "a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d",
"externalOrderId": "partner-order-123",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"prescriber": {
"userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"firstName": "Alex",
"lastName": "Reed",
"npi": "1234567890"
},
"status": "Shipped",
"shippingMethod": "Standard",
"createdAt": "2026-06-05T12:30:00.0000000+00:00",
"metadata": {
"key1": "value1",
"key2": "value2"
},
"items": [
{
"orderItemId": "c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f",
"medicationId": "7c9e6a1b-1f4d-4a2e-9c3b-9a1d2e3f4a5b",
"medicationName": "Semaglutide 2.5mg/mL",
"directions": "Take one capsule daily",
"externalPrescriptionId": "rx-001",
"pharmacyNotes": "Clinical Difference Statement: Patient requires compounded formulation due to clinical need.",
"isSupply": false,
"status": "Shipped"
}
],
"shipments": [
{
"shipmentId": "shp_d3e4f5a6b7c84d9e0f1a2b3c4d5e6f70",
"carrier": "UPS",
"trackingNumber": "1Z999AA10123456784",
"trackingUrl": "https://www.ups.com/track?tracknum=1Z999AA10123456784",
"orderItemIds": ["c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f"]
}
]
}
}
} {
"id": "evt_1b3d5f7a9c2e4068b1d3f5a7c9e0b2d4",
"type": "order.approved",
"environment": "live",
"createdAt": "2026-06-05T14:10:00.0000000+00:00",
"data": {
"order": {
"orderId": "a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d",
"externalOrderId": null,
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"prescriberUserId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
"status": "Pending",
"approval": {
"status": "Approved",
"submittedForApprovalAt": "2026-06-05T13:55:00.0000000+00:00",
"reviewedAt": "2026-06-05T14:10:00.0000000+00:00",
"note": null
}
}
}
} Webhook signatures
Verify the RxRelay-Signature
header before trusting a webhook. Compute HMAC-SHA256 using your endpoint secret over
{timestamp}.{raw_body}
and compare it to the v1 value.
const signedPayload = timestamp + "." + rawRequestBody;
const expected = hmacSha256(endpointSecret, signedPayload);
// compare expected to the v1 value from RxRelay-Signature Webhook events
order.sandbox_received A sandbox API order was validated and recorded without vendor dispatch.
order.sent A live order was submitted and did not end in a failed aggregate state. Delivered asynchronously after the order API response.
order.failed A live order completed with a failed aggregate state. Delivered asynchronously after the order API response.
order.tracking_updated Tracking information was received from the fulfillment network and forwarded asynchronously without exposing downstream pharmacy routing.
order.pending_approval An order was staged inside RxRelay and is awaiting prescriber approval. Nothing has been sent to a pharmacy yet. Only fires for orders created in the RxRelay app, not through this API.
order.approved A prescriber or clinic admin approved a staged order and released it for pharmacy dispatch. Distinct from order.sent, which follows once the pharmacy hand-off completes.
order.rejected A prescriber or clinic admin declined a staged order. It is never sent to a pharmacy and is not billed. Terminal — the order cannot be un-rejected.
webhook.test A test event sent from Settings / API. Test sends are attempted immediately so you can verify endpoint handling.