RxBridge API
RxBridge is a real-time e-prescribing bridge between provider (prescriber) organizations and pharmacy organizations. This document describes the public partner API under /api/v1 — the same routes documented at /docs/api. Internal dashboard routes (/api/internal/**) and platform-admin routes (/api/admin/**) are not part of this contract and are not included here. ## Test mode Scripts created with a test-mode API key are test scripts: they are never submitted to the pharmacy integration, generate no billing and send no patient or prescriber emails, and every webhook about them carries `livemode: false`. With a test-mode key, onboarding checks the NPI checksum only (no registry lookup), and it cannot modify a prescriber that has been verified against the NPI registry (409 `live_prescriber_protected`). A test-mode key also cannot update the status or shipment of a live script (403 `live_script_protected`); a live key may act on a test script. ## Error codes Error bodies are `{ error, code?, field? }`. `code` is a stable machine-readable identifier — branch on it, not on the message. | code | HTTP status | Meaning | |---|---|---| | `invalid_json` | 400 | POST /scripts, POST /onboarding/prescriber, PATCH /onboarding/licenses, POST /onboarding/sessions: the request body is not valid JSON (onboarding also requires a JSON object). | | `prescriber_required` | 400 | POST /scripts: the organization has more than one active prescriber and the request named none. Send prescriber_id or prescriber_npi; the response lists active_prescribers. | | `invalid_prescriber_npi` | 400 | POST /scripts: prescriber_npi is not a 10-digit NPI. | | `prescriber_selector_conflict` | 400 | POST /scripts: prescriber_id and prescriber_npi identify different prescribers. | | `prescriber_not_found` | 404 | The prescriber_id / prescriber_npi (POST /scripts) or prescriber_id (PATCH /onboarding/licenses) is not a prescriber in your organization. | | `no_license_for_state` | 422 | POST /scripts: the prescriber has no unexpired license in the patient's state, patientState is not a valid US state or territory code, or patientState is required because the prescriber holds unexpired licenses in several states. | | `no_prescriber` | 403 | POST /scripts: the organization has no prescriber that can sign scripts. | | `prescriber_pending_review` | 403 | POST /scripts: the prescriber was submitted but a platform admin has not approved it yet. | | `prescriber_rejected` | 403 | POST /scripts: the prescriber failed platform review. | | `prescriber_deactivated` | 403 | POST /scripts: the prescriber was approved but has since been deactivated (an admin action, or an expired DEA registration). | | `license_expired` | 403 / 400 | 403 on POST /scripts: all of the prescriber's licenses have expired. 400 on onboarding and license writes: a submitted license expiration_date is in the past. | | `prescriber_npi_unverified` | 403 | POST /scripts with a live key: the prescriber was activated in test mode without NPI registry verification, so it can only sign test scripts. Once its organization has a live key it cannot sign until its NPI is verified. | | `medication_unavailable` | 400 | POST /scripts: medicationId is not an available product at the script's pharmacyOrgId. | | `medication_mismatch` | 400 | POST /scripts: the strength sent does not match the strength of the catalog product named by medicationId. | | `live_prescriber_protected` | 409 | POST /onboarding/prescriber, PATCH /onboarding/licenses: a test-mode API key tried to modify a prescriber that has been verified against the NPI registry. | | `live_script_protected` | 403 | PATCH /scripts/{id}/status, POST /scripts/{id}/shipment: a test-mode API key tried to modify a live script. Test keys can only modify test scripts. | | `invalid_patient_state` | 400 | GET /pharmacies/recommend: patient_state is missing or is not a two-letter US state or territory code. | | `invalid_medication_id` | 400 | GET /pharmacies/recommend: medication_id is not a valid UUID. | | `provider_org_required` | 403 | The onboarding endpoint is only available to provider organizations. | | `full_legal_name_required` | 400 | full_legal_name is missing. | | `mobile_phone_required` | 400 | mobile_phone is missing. | | `invalid_mobile_phone` | 400 | mobile_phone is not a 10-digit US number. | | `professional_email_required` | 400 | professional_email is missing. | | `npi_required` | 400 | npi is missing. | | `invalid_npi` | 400 | npi (body or ?npi= filter) is not exactly 10 digits, or its checksum is invalid. | | `npi_not_found` | 400 | The NPI is not in the NPPES registry (live keys). | | `npi_inactive` | 400 | The NPI is deactivated in the NPPES registry (live keys). | | `npi_not_individual` | 400 | The NPI belongs to an organization (NPI-2); an individual (NPI-1) is required (live keys). | | `npi_lookup_failed` | 502 | The live NPPES registry lookup itself failed; safe to retry. | | `licenses_required` | 400 | licenses must be a non-empty array. | | `invalid_license` | 400 | A licenses[] entry is not an object. | | `license_number_required` | 400 | licenses[].license_number is missing. | | `invalid_license_state` | 400 | licenses[].state is not a 2-letter code for one of the 50 states, DC or PR. | | `invalid_license_type` | 400 | licenses[].license_type is not physician, aprn, pa or pharmd (or an accepted alias). | | `license_expiration_required` | 400 | licenses[].expiration_date is missing. | | `invalid_license_expiration` | 400 | licenses[].expiration_date is not a valid YYYY-MM-DD date. | | `duplicate_license_state` | 400 | Two licenses in the payload have the same state. | | `invalid_date_of_birth` | 400 | date_of_birth is not a valid date, or the prescriber is under 18. | | `invalid_dea_number` | 400 | The DEA number is malformed or fails the checksum / last-name check. | | `invalid_dea_expiration_date` | 400 | The DEA expiration date is not a valid YYYY-MM-DD date. | | `attestations_required` | 400 | The attestations object is missing. | | `attestation_required` | 400 | One of the required attestations is not true. | | `typed_signature_required` | 400 | typed_signature is missing. | | `prescriber_id_required` | 400 | PATCH /onboarding/licenses: prescriber_id is missing. | | `invalid_prescriber_id` | 400 | GET /onboarding/prescriber: the prescriber_id filter is not a UUID. | | `return_url_required` | 400 | POST /onboarding/sessions: return_url is missing. | | `invalid_return_url` | 400 | POST /onboarding/sessions: return_url is not an absolute http(s) URL, or carries credentials, a fragment, or one of the reserved query parameters session_id, state or status. | | `invalid_prefill` | 400 | POST /onboarding/sessions: a prefill value is malformed (wrong type, empty, or over-long) and has no more specific code. The field is named in `field`. | | `prefill_field_not_allowed` | 400 | POST /onboarding/sessions: prefill.attestations or prefill.typed_signature was sent. The prescriber must enter both personally on the hosted page. | | `return_url_not_allowed` | 403 | POST /onboarding/sessions: return_url's origin is not registered for your organization or its partner (an RxBridge admin registers return origins), or it is http / test-only for a live key. | | `invalid_expires_in` | 400 | POST /onboarding/sessions: expires_in is not an integer number of seconds in [300, 604800]. | | `external_ref_required` | 400 | POST /onboarding/sessions: external_ref is missing. | | `invalid_external_ref` | 400 | POST /onboarding/sessions: external_ref is not a non-empty string of at most 200 characters. | | `invalid_state` | 400 | POST /onboarding/sessions: state is not a string of at most 500 characters. | | `session_already_open` | 409 | POST /onboarding/sessions: an open session already exists for this external_ref. The body carries session_id and expires_at; DELETE it or pass replace: true. | | `invalid_session_id` | 400 | GET/DELETE /onboarding/sessions/{id}: the path id is not a UUID. | | `session_not_found` | 404 | GET/DELETE /onboarding/sessions/{id}: no such session for this organization (an unknown id and another organization's id are indistinguishable). | | `session_already_completed` | 409 | DELETE /onboarding/sessions/{id}: the session is already completed and cannot be canceled. | | `mode_mismatch` | 409 | POST /provision: the email's existing organization has API keys in the other mode (live vs test), or test_mode was requested for an organization with no keys that already has live data (a live script or an NPI-verified prescriber), so no key was created. Use a different email for the other mode. | | `org_not_owned_by_partner` | 403 | POST /provision: the email's existing organization was not created by the calling partner (matched by external_source), so no key was created. Only the partner that created an organization can re-provision it; its owner manages keys in Rx-Bridge. | | `source_mismatch` | 403 | POST /provision: the request names a partner (external_source) other than the one the provisioning key belongs to, so nothing was created. | | `partner_key_required` | 403 | POST /provision: external_org_id was sent with the shared provisioning key. It needs a provisioning key issued to your integration only, since the shared key cannot say which partner is calling. | | `partner_not_registered` | 409 | POST /provision: external_org_id was sent but the calling integration is not registered as a partner in Rx-Bridge, so no organization was created. | | `no_active_contract` | 403 | POST /scripts: the provider has no active contract with the pharmacy named in pharmacyOrgId, so nothing was created or sent. Request or accept a contract first. | | `onboarding_failed` | 500 | POST /onboarding/prescriber failed; nothing was saved. | | `update_failed` | 500 | PATCH /onboarding/licenses failed (licenses may be saved when a later DEA write fails; the retry is idempotent). | | `lookup_failed` | 500 | A read of prescribers or licenses failed. |
The RxBridge REST API lets you integrate prescription management into your application. Send scripts, track fulfillment, manage contracts, and receive real-time webhook notifications.
Authentication
All API endpoints require authentication via an API key. Include it in one of these headers:
Authorization: Bearer rxb_your_api_key_here
# or
x-api-key: rxb_your_api_key_hereAPI Key Scopes
API keys can be scoped to limit what actions they can perform. An unscoped key (or one with no scopes set) has full access. When creating a key, you can restrict it to specific scopes:
| Scope | Allows |
|---|---|
| scripts:read | List and get script details |
| scripts:write | Create scripts |
| pharmacies:read | Search pharmacies |
| contracts:read | List and get contracts |
| contracts:write | Create and update contracts |
| webhooks:manage | Register, update, and delete webhooks |
| onboarding:manage | Complete prescriber onboarding and update licenses |
| * | Full access (all scopes) |
If a key with limited scopes attempts a forbidden action, the API returns 403 Insufficient scope.
Sandbox / Test Mode
You can create test API keys for development without affecting production data. Test keys are flagged with isTestKey: true and behave identically to production keys but are visually distinguishable in audit logs and dashboards.
To create a test key, set is_test_key: true when generating a key via the API Keys settings page.
Rate Limits
The API enforces per-organization rate limits using a sliding window. Every response includes rate limit headers so you can monitor your usage: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
| Tier | Limit | Applies to |
|---|---|---|
| Standard | 100 / minute | GET endpoints (list, search, details) |
| Write | 30 / minute | POST/PATCH (create scripts, update status) |
| Webhook | 20 / minute | Webhook management endpoints |
If you exceed the limit, the API returns 429 Too Many Requests. Use the X-RateLimit-Reset header to know when to retry.
Pagination
List endpoints return paginated results. Use the page and limit query parameters to control pagination (page defaults to 1, limit defaults to 25 and maxes out at 100).
{
"data": [ ... ],
"pagination": {
"page": 2,
"limit": 10,
"total": 47,
"total_pages": 5,
"has_more": true
}
}CORS
All /api/v1/* endpoints include CORS headers, allowing browser-based integrations to call the API directly. Preflight OPTIONS requests are handled automatically.
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, x-api-key
Access-Control-Max-Age: 86400Naming Conventions
The API uses two naming conventions depending on the resource:
| Resource | Request fields | Response fields |
|---|---|---|
| Scripts | camelCase (pharmacyOrgId, patientName) | camelCase (medicationName, createdAt) |
| Contracts | snake_case (pharmacy_org_id) | snake_case (provider_org_name, created_at) |
| Webhooks | snake_case (is_active) | snake_case (created_at, is_active) |
| Onboarding | snake_case (full_legal_name) | snake_case (prescriber_id, is_active) |
| Pagination | — | snake_case (total_pages, has_more) |
Error Handling
The API uses standard HTTP status codes. Error responses include a JSON body:
{
"error": "Human-readable error message",
"details": { "field": ["Validation error"] }
}| Status | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad request / validation error |
| 401 | Invalid or missing API key |
| 403 | Forbidden (wrong org type, insufficient scope, expired license) |
| 404 | Resource not found |
| 429 | Rate limit exceeded — check X-RateLimit-Reset header |
| 500 | Server error — retry with backoff |
| 502 | Upstream lookup failed (e.g. NPI registry unavailable during prescriber onboarding) — safe to retry |
Contracts
4 endpointsProvider organizations only. Creates a pending contract request between the caller's provider org and the given pharmacy org. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Organization ID of the pharmacy to request a contract with.
Returns contracts where the caller's organization is either the provider or the pharmacy party. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
action determines which lifecycle transition to perform; start_date/end_date/notes are only meaningful for the matching action. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Used only when action is "accept".
Used only when action is "terminate".
Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Organization
1 endpointReturns the organization the calling API key belongs to. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Medications
1 endpointFor pharmacy organizations, returns medications stocked (pharmacy_medications.is_available = true) at their own pharmacy. For provider organizations, returns medications available across every pharmacy they have an active contract with. Results are deduplicated by medication ID; when several pharmacies stock the same product, the ordering limits (quantityOptions, daysSupplyOptions, maxRefills, requiresPriorAuth, shipsToStates, leadTimeDays) come from the first pharmacy returned. Pharmacy-internal fields (LifeFile product id, price, product code) are never exposed. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Onboarding
7 endpointsProvider organizations only. Upserts state license rows by (prescriber_id, state) — other states are kept — and upserts the single DEA registration row for the prescriber. Use for renewals or adding a new state license without re-running full onboarding. Every license is validated before anything is written (a bad entry fails the whole request instead of being skipped), and the license upsert is a single atomic statement. Licenses use the same rules as POST /onboarding/prescriber. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Must belong to the caller's organization.
Optional. Each entry is validated; duplicate states are rejected.
Validated against the prescriber's last name via validateDEANumber (DEA checksum algorithm).
Free-form; an array is stored comma-joined.
Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Provider organizations only. Upserts a prescriber identified by (organization, npi) with identity, licenses, optional DEA registration, and an attestation, in one atomic request: either everything is saved or the request fails. An organization may hold many prescribers. A new NPI creates an inactive prescriber with review_status "pending" (201); an existing NPI updates that prescriber (200). licenses is the prescriber's complete set: states not listed are removed. On an already-approved prescriber, a substantive change to full_legal_name resets it to pending review and deactivates it until a platform admin re-approves. The comparison ignores case, punctuation, titles and credentials (Dr, MD, DO, NP and similar) and tolerates added name parts such as a middle name or initial, so only dropping or replacing a name part of the stored name triggers re-review. A change in the name the NPI registry holds for the NPI (live keys) is compared the same way. Other field changes never affect activation. New prescribers require platform review before they can be used to create scripts. The NPI must belong to an individual (NPI-1) and is checked against the NPPES registry in live mode (sandbox/test-mode API keys only run the local checksum); a registry name that differs from full_legal_name does not reject the request but flags the prescriber for the reviewer. Test-mode API keys cannot modify a prescriber whose NPI has been verified (409 live_prescriber_protected). Identity verification (Stripe) is not required on this path. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Optional, YYYY-MM-DD. If given it must be a valid date and the prescriber at least 18.
A 10-digit US number; formatting characters and a leading country code 1 are ignored.
Exactly 10 digits, no separators. Must be an individual NPI (NPI-1) that passes the NPPES lookup (live keys) or the checksum (test keys).
The complete license set (replace semantics). At least one; no duplicate states.
Validated (DEA checksum and last-name letter) only when will_prescribe_controlled_substances is true and dea_number is present.
YYYY-MM-DD.
Free-form; an array is stored comma-joined.
All four keys must be true.
False until a platform admin reviews and activates the prescriber.
Derived status: active if is_active; else rejected if review_status is rejected; else deactivated if review_status is approved; else pending_review.
False until a platform admin reviews and activates the prescriber.
Derived status: active if is_active; else rejected if review_status is rejected; else deactivated if review_status is approved; else pending_review.
Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Provider organizations only. Returns a single-use URL for RxBridge's hosted onboarding page: hand it to the prescriber (RxBridge sends no email). The URL is returned once; treat it as a credential. The prescriber completes identity, NPI and licenses, DEA (if needed), the four attestations and a typed signature on RxBridge; they always enter the attestations and signature themselves, so those cannot be prefilled. `return_url` must match an origin an RxBridge admin registered for your organization or its partner (a partner that provisions organizations through the provisioning API has its origins registered once, and every organization it provisions inherits them); without a registered origin no session can be created. On completion the prescriber is redirected to `return_url` with only `session_id`, `state` and `status`; the authoritative result is the `onboarding_session.completed` webhook and `GET /onboarding/sessions/{id}`. Completion means the prescriber entered pending review, never approval. The session inherits test or live mode from the API key. Prefill values are validated strictly: `null` and empty strings for `npi`, `mobile_phone`, `date_of_birth` and `professional_email` are rejected with `invalid_prefill` (or the field's own code, such as `invalid_npi`); omit a key to skip it. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 30 requests / 60s per organization (tier: `write`).
Absolute URL on a registered origin, at most 2000 characters. Must be https, except that http is accepted for loopback origins (localhost, 127.0.0.1, [::1]) registered test-only and only with a test-mode API key. No fragment (not even an empty `#`), credentials, or `session_id` / `state` / `status` query parameters.
Your identifier for this prescriber. At most one open session per external_ref.
Opaque value echoed back on the redirect.
Seconds the link stays valid.
Cancel an open session for the same external_ref and mint a new one.
Advisory, editable prescriber data. Never verified, never placed in a URL. `attestations` and `typed_signature` are rejected.
May be empty or partial; every entry present is validated in full.
Array or comma-separated string.
Returns the session state. `status` is evaluated against `expires_at` on every read, so an overdue open session reads `expired`. Once completed, `prescriber_id`, `prescriber_status`, `review_status` and a license summary (no license numbers) are included. Another organization's session id returns 404 like an unknown id. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Withdraws an open session (also releases its external_ref). Idempotent on an already-canceled session; an already-expired session is returned as `expired`. A completed session cannot be canceled. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 30 requests / 60s per organization (tier: `write`).
Pharmacies
2 endpointsReturns every pharmacy on Rx-Bridge (not scoped to the caller's contracts), optionally filtered. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Provider organizations only. Returns the best pharmacy to route a script to, with a short `reason`, plus up to four `alternatives`. Only active, onboarded pharmacies with an unexpired primary license that serve `patient_state` (states served, or an unexpired pharmacy license or DEA registration there) are considered; when `medication_id` is sent, the pharmacy must also list that medication as available. Ranking: an active contract with your organization first, then more services offered, then name. `id` is the pharmacy organization id, the value `POST /scripts` takes as `pharmacyOrgId`. `recommendation` is null (and `alternatives` empty) when no pharmacy qualifies. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Pricing
1 endpointProvider organizations get providerFee (and providerFeeBreakdown, if includeBreakdown is true) based on their current-month script volume plus add-ons. Pharmacy organizations get pharmacyCommission when orderValue is given. Fields are populated based on the caller's organization type — a pharmacy org never gets providerFee, a provider org never gets pharmacyCommission. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Provider organizations only. Note: as of this API version every add-on fee is $0 (ADD_ON_FEES in lib/pricing/pharmacy.ts) — pricing is reserved for future use but not yet active.
Pharmacy organizations only. Required to receive a pharmacyCommission estimate.
Provider organizations only. When true, adds providerFeeBreakdown to the response.
Volume-tiered base fee plus add-on fees, rounded to 2 decimals. See lib/pricing/pharmacy.ts's VOLUME_TIERS.
Only present when includeBreakdown is true.
6% of orderValue, clamped to [$1.00, $20.00], rounded to 2 decimals.
Scripts
7 endpointsProvider organizations only. Requires an active, onboarded prescriber: with exactly one it is used automatically; with several, name one with prescriber_id or prescriber_npi (otherwise 400 prescriber_required). The prescriber must hold an unexpired license in the patient's state (patientState); that license is stored on the script and sent on the pharmacy order (422 no_license_for_state). If patientState is omitted the prescriber's only unexpired license is used, and a prescriber licensed in several states must send patientState. Validates NCPDP rules (Schedule II refills/quantity, NDC format, DAW code, ICD-10 diagnosis codes) and that the pharmacy and prescriber licenses are current. On success, asynchronously submits to LifeFile when eligible and fires script.created webhooks (and script.approval_requested, if requiresPrescriberApproval was set) to the provider org and, if set, the pharmacy org. Scripts created with a test-mode API key are never submitted to the pharmacy integration and their webhooks carry livemode: false. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **provider** organization. Rate limit: 30 requests / 60s per organization (tier: `write`).
ISO date. Not required to create a script, but LifeFile order submission requires it — see lib/lifefile/order-mapping.ts.
Optional catalog product id (`id` from GET /v1/medications). Must be an available product at the script's pharmacy, else 400. When set, the pharmacy's LifeFile product id is sent with the order.
10 digits (4-4-2) or 11 digits (5-4-2); hyphens optional.
Must be 0 for Schedule II (controlledSubstanceSchedule "2"/"II") — NCPDP rule.
0-9.
Required (positive number) for Schedule II — NCPDP rule.
ICD-10-CM codes, e.g. "A00.0".
Set true when the submitter is not the assigned prescriber, to route the script through prescriber sign-off before pharmacy fulfillment.
Display/audit-only.
Caller's own opaque submitter id. Never resolved to an Rx-Bridge user.
Optional free-text clinical context from the submitter, intended for the approving prescriber. Trimmed; an empty value is stored as null; more than 2000 characters returns 400 with details.clinicalNote. Returned as `clinicalNote` on the script, to the provider organization's keys only (always null for the pharmacy organization).
Which of the organization's prescribers signs this script. Optional when the organization has exactly one active prescriber; required (this or prescriber_npi) when it has several. Send at most one of prescriber_id / prescriber_npi (both is allowed only if they name the same prescriber).
10-digit NPI of the prescriber that signs this script. Alternative to prescriber_id.
Provider organizations see scripts they created; pharmacy organizations see scripts routed to them. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Caller's organization must be either the providerOrgId or pharmacyOrgId on the script. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Chronological history of a script, oldest first: submission, prescriber approval request, approval or rejection, status changes and shipment updates. Each event carries a `source` (`medoura` or `praxis` when the action came through that partner's API key, `pharmacy` when the routed pharmacy acted, otherwise `rxbridge`). `actorName` is only set for the submitter (submission, approval request) and the assigned prescriber (approval, rejection); it is null for pharmacy activity. Caller's organization must be either the providerOrgId or pharmacyOrgId on the script. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Caller's organization must be either the providerOrgId or pharmacyOrgId on the script. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Caller's organization must be either the providerOrgId or pharmacyOrgId on the script, same as GET. Any given field replaces the existing value; omitted fields (or empty strings) keep the script's current value. Fires script.shipment_updated webhooks to the provider and pharmacy orgs on the script. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Pharmacy organizations only, and only for scripts routed to their own organization. Transitions must be legal per lib/workflow/script-status.ts's ALLOWED_TRANSITIONS (e.g. a script can't jump straight from "pending" to "completed"). Two controlled-substance-only compliance gates apply on the transition that first reaches the target status (not on same-status "re-save" calls): moving into "verified" requires a diversion red-flag review (redFlagReviewConfirmed + reviewedByUserId) when controlledSubstanceSchedule is set; moving into "completed" requires photo-ID verification (idVerificationType + idVerifiedByUserId, optionally idVerificationLast4) when controlledSubstanceSchedule is set. reviewedByUserId/idVerifiedByUserId must be a pharmacist or admin member of the caller's organization. Fires script.status_changed webhooks to the caller's org and the script's provider org. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Callers must be a **pharmacy** organization. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Target status. Must be a legal transition from the script's current status.
Persisted as the script's orderValue on every call that includes it (used for pharmacy commission once the script reaches "completed").
Required with reviewedByUserId when status is "verified" and the script is a controlled substance, on the transition into "verified".
Must be a pharmacist or admin member of the caller's organization.
Required (with idVerifiedByUserId) when status is "completed" and the script is a controlled substance, on the transition into "completed".
Last 4 characters only — never the full ID number.
Must be a pharmacist or admin member of the caller's organization.
Webhooks
5 endpointsGenerates an HMAC-SHA256 signing secret ("whsec_..."), returned only in this response — store it to verify the x-rxbridge-signature header on delivered payloads. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Non-empty. "*" subscribes to every event. contract.created, contract.accepted, contract.rejected and contract.terminated are reserved: they can be subscribed to but are not emitted yet. prescriber.* events carry data { prescriber_id, npi, status, reason?, livemode } — reason is present only on prescriber.rejected and prescriber.deactivated. onboarding_session.completed (a prescriber finished a hosted onboarding session) carries data { session_id, external_ref, prescriber_id, npi, status, livemode }; status is the prescriber's status at completion (pending_review, never approval).
Signing secrets are never included in list responses — only in the POST /webhooks response, at creation time. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Every field is optional; only fields present in the body are changed. The signing secret cannot be rotated via this endpoint. Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).
Non-empty if given.
Requires an organization API key via `Authorization: Bearer <key>` or `x-api-key: <key>`. Rate limit: 100 requests / 60s per organization (tier: `standard`).