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

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 header
Authorization: Bearer rxrelay_sandbox_your_key

Authorizations

Authorization string header required
Bearer token using your clinic API key, e.g. 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 Too Many Requests
A rate-limited response includes 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.

Problem Details
{
  "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. 1. Create a sandbox read_write key from Settings → API.
  2. 2. Fetch GET /v1/medications and submit representative sandbox orders with required idempotency keys.
  3. 3. Add a webhook endpoint in Settings → API, copy the one-time endpoint secret, and send a test event to verify signature handling.
  4. 4. RxRelay reviews the recorded sandbox orders and webhook deliveries for the clinic.
  5. 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.

GET /v1/clinic
Request
cURL
curl https://api.rxrelay.ai/v1/clinic \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
{
  "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 string · uuid
Your RxRelay clinic id. Not required on clinic-scoped calls (the key implies it), but useful for reference.
name string
Clinic name.
externalClinicId string | null
Your own identifier for the clinic, if one was set when it was created under an organization. null for standalone clinics.
address1 string | null
Street address.
address2 string | null
Apartment, suite, unit, etc.
city string | null
City.
state string | null
Two-letter US state code.
zip string | null
ZIP / postal code.
phoneNumber string | null
Clinic phone number.
contactEmail string | null
Clinic contact email.
isSandboxMode boolean
Whether the clinic is in sandbox mode.
createdAt string · date-time
When the clinic was created.

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).

GET /v1/medications
Request
cURL
curl https://api.rxrelay.ai/v1/medications \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Filter by pharmacy
cURL
curl "https://api.rxrelay.ai/v1/medications?pharmacyId=Kaduceus:opbrx" \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
[
  {
    "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 string | null query
Restrict the catalog to a single dispensing pharmacy, using an 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 string · uuid | null query
Organization keys only. The RxRelay clinic to scope to. Provide this or externalClinicId. Ignored for clinic-scoped keys.
externalClinicId string | null query
Organization keys only. Your own identifier for the target clinic.

Response

[] object[]
An array of medications available to this clinic.
Show child attributes
id string · uuid
Medication ID. Use this as items[].medicationId when creating an order.
name string
Display name of the medication.
strength string | null
Strength / concentration (e.g. 2.5 mg/mL), when the pharmacy provides it.
form string | null
Dosage form (e.g. Injectable, Tablet), when available.
pharmacyName string | null
Display name of the fulfilling pharmacy for this medication.
pharmacySku string | null
The fulfilling pharmacy's own product identifier (the downstream SKU) for this medication, when available.
ingredients object[]
Structured composition, when the pharmacy provides it (empty otherwise). For a multi-active blend, use this to show what's actually in the vial — strength alone can't describe it. Not required to order.
Show child attributes
name string
Ingredient (active) name, e.g. BPC-157.
concentration number | null
Amount per mL (or per vial for mg units).
unit string | null
Concentration unit, e.g. 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.

GET /v1/pharmacies
Request
cURL
curl https://api.rxrelay.ai/v1/pharmacies \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
[
  {
    "id": "Boothwyn",
    "name": "Boothwyn Pharmacy",
    "platform": "Boothwyn"
  },
  {
    "id": "Kaduceus:opbrx",
    "name": "Kaduceus Pharmacy",
    "platform": "Kaduceus"
  }
]

Response

[] object[]
An array of pharmacies this clinic can dispense from.
Show child attributes
id string
Opaque, stable pharmacy identifier. Treat it as a string and pass it back verbatim as pharmacyId on List medications.
name string | null
Friendly display name of the pharmacy, when one has been assigned.
platform string
The fulfillment network the pharmacy belongs to.

List prescribers

Returns prescribers in the clinic that have an NPI on file, ordered by name.

GET /v1/prescribers
Request
cURL
curl https://api.rxrelay.ai/v1/prescribers \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
[
  {
    "userId": "3f1a8c7d-2b4e-4f6a-9c8d-7e6f5a4b3c2d",
    "firstName": "Alex",
    "lastName": "Reed",
    "email": "alex.reed@northsideclinic.com",
    "npi": "1234567890",
    "licenseState": "TX",
    "licenseNumber": "MD-44821"
  }
]

Response

[] object[]
An array of prescribers eligible to be referenced on an order.
Show child attributes
userId string · uuid
Prescriber's RxRelay user ID. Pass as prescriberUserId when creating an order.
firstName string | null
Prescriber's first name.
lastName string | null
Prescriber's last name.
email string | null
Prescriber's email address.
npi string
National Provider Identifier. Pass as prescriberNpi when creating an order.
licenseState string | null
State that issued the prescriber's license.
licenseNumber string | null
Prescriber's license number.

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.

POST /v1/users
Request
cURL
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"
}'
Response
200
{
  "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 string body required
The user's email. Matches an existing RxRelay user or creates one.
firstName string | null body
User's first name.
lastName string | null body
User's last name.
roles string[] body required
Clinic roles: prescriber, clinic_admin, staff, or owner. platform_admin cannot be assigned via the API.
npi string | null body
Required when roles includes prescriber.
licenseState string | null body
State that issued the prescriber's license.
licenseNumber string | null body
Prescriber's license number.
phone string | null body
User phone number.
deaNumber string | null body
DEA number.

Response

userId string · uuid
The user's RxRelay ID.
email string
The user's email.
firstName string | null
User's first name.
lastName string | null
User's last name.
roles string[]
The user's roles in this clinic.
npi string | null
National Provider Identifier, if set.
joinedAt string · date-time
When the user joined the clinic.

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.

GET /v1/users
Request
cURL
curl https://api.rxrelay.ai/v1/users \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
[
  {
    "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

[] object[]
An array of the clinic's users, ordered by name.
Show child attributes
userId string · uuid
The user's RxRelay ID.
email string
The user's email.
firstName string | null
User's first name.
lastName string | null
User's last name.
roles string[]
The user's roles in this clinic.
npi string | null
National Provider Identifier, if set.
joinedAt string · date-time
When the user joined the clinic.

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.

GET /v1/patients
Request
cURL
curl "https://api.rxrelay.ai/v1/patients?search=jane&limit=25&offset=0" \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
{
  "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 string · uuid | null query
Organization keys only. The RxRelay clinic to scope to. Provide this or externalClinicId. Ignored for clinic-scoped keys.
externalClinicId string | null query
Organization keys only. Your own identifier for the target clinic.
search string | null query
Case-insensitive substring over name / email / phone, plus an exact date-of-birth match (e.g. 1980-01-10).
email string | null query
Exact (case-insensitive) email match.
externalPatientId string | null query
Exact match on your patient identifier.
limit integer query default: 25
Page size, between 1 and 100.
offset integer query default: 0
Number of records to skip.

Response

patients object[]
The page of matching patients.
Show child attributes
patientId string · uuid
RxRelay patient ID.
externalPatientId string | null
Your mapped patient identifier, if one exists.
firstName string
Patient's first name.
lastName string
Patient's last name.
dateOfBirth string · date
ISO date, e.g. 1980-01-10.
sex string | null
One of M, F, or U.
phone string | null
Patient phone number.
email string | null
Patient email.
createdAt string · date-time
When the patient was created.
addresses object[]
The patient's active addresses, default first. Same shape as Patient addresses.
total integer
Total matching patients across all pages.
limit integer
The effective page size applied.
offset integer
The offset applied.

Get a patient

Fetches a single patient by RxRelay ID. Returns 404 Not Found if the patient does not belong to your clinic.

GET /v1/patients/{patientId}
Request
cURL
curl https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
{
  "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 string · uuid path required
The RxRelay patient ID.

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.

POST /v1/patients
Request
cURL
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
  }
}'
Response
200
{
  "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"
    }
  ]
}
Request body
JSON
{
  "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 string | null body
Your stable patient identifier. Used for upsert matching within the clinic.
firstName string | null body
Required when creating a new patient.
lastName string | null body
Required when creating a new patient.
dateOfBirth string · date | null body
ISO date, e.g. 1980-01-10. Required when creating a new patient.
sex string | null body
One of M, F, or U.
phone string | null body
Patient phone number.
email string | null body
Patient email. Used as a fallback match when externalPatientId is absent.
defaultAddress object | null body
An optional default address to store on the new or resolved patient.
Show child attributes
address1 string required
Street address.
address2 string | null
Apartment, suite, unit, etc.
city string required
City.
state string required
Two-letter US state code, e.g. TX.
zip string required
ZIP / postal code.
isDefault boolean default: true
Whether to make this the patient's default address.

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.

PATCH /v1/patients/{patientId}
Request
cURL
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"
}'
Response
200
{
  "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"
    }
  ]
}
Request body
JSON
{
  "phone": "5125550199",
  "email": "jane.doe@example.com",
  "externalPatientId": "patient-456"
}

Body

firstName string | null body
New first name.
lastName string | null body
New last name.
dateOfBirth string · date | null body
New date of birth, ISO date.
sex string | null body
One of M, F, or U.
phone string | null body
New phone number.
email string | null body
New email.
externalPatientId string | null body
(Re)maps your patient identifier to this patient. 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.

POST /v1/orders
Request
cURL
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
    }
  ]
}'
Response
201
{
  "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"]
    }
  ]
}
Alternative request body
shippingAddressId
{
  "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 string header required
Bearer token using a read_write API key.
Idempotency-Key string header required
Unique key for this order. Replaying the same key with the same body returns the original order; reusing it with a different body returns 409 Conflict.

Body

clinicId string · uuid | null body
Organization keys only. The RxRelay clinic the order is for. Provide this or externalClinicId. Ignored for clinic-scoped keys.
externalClinicId string | null body
Organization keys only. Your own identifier for the target clinic. Provide this or clinicId.
externalOrderId string | null body
Your identifier for the order. Stored and echoed back on the order response.
metadata Record<string, string | number | boolean | null> | null body
Optional display and audit context stored with the order, shown in RxRelay, returned by order APIs, and included in order webhooks. Metadata is not used for auth, routing, billing, or fulfillment. Limits: 20 keys, 64 characters per key, 512 characters per value; nested objects and arrays are rejected.
patient object body required
The patient this order is for. Used to match or create a patient in the clinic.
Show child attributes
patientId string · uuid | null
Match an existing RxRelay patient directly. When set, other matching fields are ignored.
externalPatientId string | null
Your stable patient identifier. Primary key used for upsert matching within the clinic.
firstName string | null
Required when creating a new patient.
lastName string | null
Required when creating a new patient.
dateOfBirth string · date | null
ISO date, e.g. 1980-01-10. Required when creating a new patient.
sex string | null
One of M, F, or U.
phone string | null
Patient phone number.
email string | null
Patient email. Used as a fallback match when externalPatientId is absent.
shippingAddress object body
Where the order ships. Provide exactly one of 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 string required
Street address.
address2 string | null
Apartment, suite, unit, etc.
city string required
City.
state string required
Two-letter US state code, e.g. TX.
zip string required
ZIP / postal code.
shippingAddressId string · uuid | null body
Existing RxRelay patient address ID. Provide exactly one of shippingAddressId or shippingAddress. The address must belong to the resolved patient and be active.
prescriberUserId string · uuid | null body
Prescriber's RxRelay user ID. Provide this or prescriberNpi; the prescriber must belong to the clinic.
prescriberNpi string | null body
Prescriber's NPI. Used to resolve the prescriber when prescriberUserId is omitted.
shippingMethod string body default: Standard
Shipping method for the order.
items object[] body required
At least one Rx item is required.
Show child attributes
medicationId string · uuid required
A medication ID from GET /v1/medications.
medicationName string | null
Overrides the catalog name on this line. Defaults to the medication's catalog name.
quantity string required
Dispense quantity, e.g. 30.
directions string required
Sig / directions for the patient.
refills integer required
Number of refills.
externalPrescriptionId string | null
Your identifier for this prescription line. Echoed back on the item.

Response

orderId string · uuid
RxRelay order ID.
externalOrderId string | null
The externalOrderId you supplied, if any.
patientId string · uuid
The resolved or newly created patient ID.
status string
Aggregate order status — one of Queued, SandboxReceived, Pending, Sent, or Failed.
createdAt string · date-time
When the order was created.
metadata Record<string, string | number | boolean | null> | null
The metadata supplied on the order, if any.
items object[]
Per-item status.
Show child attributes
orderItemId string · uuid
RxRelay order item ID.
medicationId string · uuid
Medication on this line.
medicationName string
Resolved medication name.
externalPrescriptionId string | null
The identifier you supplied for this line.
status string
Item status, or Queued before dispatch.
submissions object[]
RxRelay submission status and item correlation. Downstream pharmacy routing details are not exposed.
Show child attributes
submissionId string · uuid
RxRelay submission ID.
environment string
Sandbox or Live.
status string
Submission status.
orderItemIds string · uuid[]
Order items included in this submission.

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.

GET /v1/orders/{orderId}
Request
cURL
curl https://api.rxrelay.ai/v1/orders/a4d9f0e2-7b1c-4e3a-8f5d-2c6b9a0e1f3d \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
{
  "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 string · uuid path required
The RxRelay order ID returned when the order was created.

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.

GET /v1/orders
Request
cURL
curl "https://api.rxrelay.ai/v1/orders?status=Sent&limit=25&offset=0" \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
{
  "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 string · uuid | null query
Organization keys only. The RxRelay clinic to scope to. Provide this or externalClinicId.
externalClinicId string | null query
Organization keys only. Your own identifier for the target clinic.
status string | null query
Filter by derived status: one of Queued, Pending, Sent, Failed, Cancelled, or SandboxReceived.
patientId string · uuid | null query
Only orders for this patient.
externalOrderId string | null query
Exact match on your order identifier.
from string · date-time | null query
Only orders created at or after this timestamp.
to string · date-time | null query
Only orders created at or before this timestamp.
limit integer query default: 25
Page size, between 1 and 100.
offset integer query default: 0
Number of records to skip.

Response

orders object[]
The page of order summaries, newest first.
Show child attributes
orderId string · uuid
RxRelay order ID.
externalOrderId string | null
The identifier you supplied, if any.
patientId string · uuid
The patient on the order.
status string
Derived order status.
createdAt string · date-time
When the order was created.
metadata Record<string, string | number | boolean | null> | null
The metadata supplied on the order, if any.
total integer
Total matching orders across all pages.
limit integer
The effective page size applied.
offset integer
The offset applied.

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.

GET /v1/patients/{patientId}/orders
Request
cURL
curl "https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e/orders?limit=25&offset=0" \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
{
  "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 string · uuid path required
The RxRelay patient ID.

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.

GET /v1/patients/{patientId}/addresses
Request
cURL
curl https://api.rxrelay.ai/v1/patients/b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e/addresses \
  -H "Authorization: Bearer rxrelay_sandbox_your_key"
Response
200
[
  {
    "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

POST /v1/patients/{patientId}/addresses
Request
cURL
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
}'
Response
200
  {
    "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

GET /v1/patients/{patientId}/addresses/{addressId}
Request
cURL
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"
Response
200
  {
    "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.

DELETE /v1/patients/{patientId}/addresses/{addressId}
Request
cURL
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.

POST /v1/patients/{patientId}/addresses/{addressId}/default
Request
cURL
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"
Response
200
  {
    "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 string · uuid
RxRelay address ID. Pass as shippingAddressId when creating an order.
patientId string · uuid
The patient this address belongs to.
address1 string
Street address.
address2 string | null
Apartment, suite, unit, etc.
city string
City.
state string
Two-letter US state code.
zip string
ZIP / postal code.
isDefault boolean
Whether this is the patient's default address.
isActive boolean
Whether the address is active. Deactivated addresses are false.
createdAt string · date-time
When the address was created.

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.

Request
cURL
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.

GET /v1/clinics
Request
cURL
curl https://api.rxrelay.ai/v1/clinics \
  -H "Authorization: Bearer rxrelay_sandbox_your_org_key"
Response
200
[
  {
    "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.

POST /v1/clinics
Request
cURL
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"
}'
Response
201
{
  "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 string body required
Clinic name.
externalClinicId string | null body
Your own identifier for the clinic, unique within the org. Used as the idempotency key and to reference the clinic on later calls.
address1 string | null body
Street address.
address2 string | null body
Apartment, suite, unit, etc.
city string | null body
City.
state string | null body
Two-letter US state code.
zip string | null body
ZIP / postal code.
phoneNumber string | null body
Clinic phone number.
contactEmail string | null body
Clinic contact email.

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.

POST /v1/clinics/{clinicRef}/users
Request
cURL
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"
}'
Response
200
{
  "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 string body required
The user's email. Matches an existing RxRelay user or creates one.
firstName string | null body
User's first name.
lastName string | null body
User's last name.
roles string[] body required
Clinic roles: prescriber, clinic_admin, staff, or owner. platform_admin cannot be assigned via the API.
npi string | null body
Required when roles includes prescriber.
licenseState string | null body
State that issued the prescriber's license.
licenseNumber string | null body
Prescriber's license number.
phone string | null body
User phone number.
deaNumber string | null body
DEA number.

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.

GET /v1/clinics/{clinicRef}/users
Response
200
[
  {
    "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.

Create endpoint
cURL
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"
  ]
}'
Response
200
{
  "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 string required
Absolute HTTP or HTTPS URL that receives webhook POST requests.
environment string default: both
One of sandbox, live, or both.
events string[] default: default order events
Event types the endpoint should receive.

Delivery payload

Headers
RxRelay-Event-Id: evt_f2a4c6e8d0b14d09a7c1e3f5b9a2c4d6
RxRelay-Delivery-Id: 9b8a7c6d-5e4f-3210-9876-abcdef123456
RxRelay-Timestamp: 1780691400
RxRelay-Signature: t=1780691400,v1=<hmac_sha256>
Order event payload
{
  "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"]
        }
      ]
    }
  }
}
Tracking update payload
{
  "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.

Verification
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.