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.
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",
"pharmacyName": "Boothwyn Pharmacy",
"pharmacySku": "SEMA-25-VIAL",
"ingredients": []
},
{
"id": "0b8d2f31-6a7c-4d5e-8f90-1a2b3c4d5e6f",
"name": "RENEW",
"strength": null,
"form": "Injectable",
"pharmacyName": "Kaduceus Pharmacy",
"pharmacySku": "11111222219",
"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" }
]
}
] 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. pharmacyName pharmacySku 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, and
dateOfBirth are required to create. 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",
"refills": 2
}
]
}' {
"orderId": "a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d",
"externalOrderId": "partner-order-123",
"patientId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"status": "SandboxReceived",
"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",
"externalPrescriptionId": "rx-001",
"status": "SandboxReceived"
}
],
"submissions": [
{
"submissionId": "d3e4f5a6-b7c8-4d9e-0f1a-2b3c4d5e6f70",
"environment": "Sandbox",
"status": "SandboxReceived",
"orderItemIds": ["c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f"]
}
]
} {
"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. directions required refills required externalPrescriptionId Response
orderId externalOrderId externalOrderId you supplied, if any. patientId status Queued, SandboxReceived, Pending, Sent, or Failed.
createdAt metadata items Show child attributes
orderItemId medicationId medicationName externalPrescriptionId status Queued before dispatch. submissions Show child attributes
submissionId environment Sandbox or Live. status orderItemIds Errors
400 Bad Request
Missing Idempotency-Key, a missing or invalid shipping address, an unknown or unsupported medication, or no resolvable prescriber.
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",
"status": "SandboxReceived",
"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",
"externalPrescriptionId": "rx-001",
"status": "SandboxReceived"
}
],
"submissions": [
{
"submissionId": "d3e4f5a6-b7c8-4d9e-0f1a-2b3c4d5e6f70",
"environment": "Sandbox",
"status": "SandboxReceived",
"orderItemIds": ["c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f"]
}
]
} 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.
List orders
Lists or searches the clinic's orders, newest first, with pagination. Each entry is a lightweight summary;
fetch a single order via GET /v1/orders/{orderId} for full item and submission detail.
/v1/orders curl "https://api.rxrelay.ai/v1/orders?status=Sent&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",
"status": "Sent",
"createdAt": "2026-06-05T12:30:00.0000000+00:00",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"total": 1,
"limit": 25,
"offset": 0
} Query parameters
clinicId externalClinicId. externalClinicId status Queued, Pending, Sent, Failed, Cancelled, or SandboxReceived. patientId externalOrderId from to limit offset Response
orders Show child attributes
orderId externalOrderId patientId status createdAt metadata 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",
"status": "Sent",
"createdAt": "2026-06-05T12:30:00.0000000+00:00",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}
],
"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"
]
}' {
"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"
],
"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",
"status": "SandboxReceived",
"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",
"externalPrescriptionId": "rx-001",
"status": "SandboxReceived"
}
],
"submissions": [
{
"submissionId": "d3e4f5a6-b7c8-4d9e-0f1a-2b3c4d5e6f70",
"environment": "Sandbox",
"status": "SandboxReceived",
"orderItemIds": ["c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f"]
}
]
}
}
} {
"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",
"status": "Shipped",
"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",
"externalPrescriptionId": "rx-001",
"status": "Shipped"
}
],
"shipments": [
{
"shipmentId": "shp_d3e4f5a6b7c84d9e0f1a2b3c4d5e6f70",
"carrier": "UPS",
"trackingNumber": "1Z999AA10123456784",
"trackingUrl": "https://www.ups.com/track?tracknum=1Z999AA10123456784",
"orderItemIds": ["c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f"]
}
]
}
}
} 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.
webhook.test A test event sent from Settings / API. Test sends are attempted immediately so you can verify endpoint handling.