Skip to main content

Webhooks

Webhooks let you receive real-time HTTP notifications when events happen in RxScale. Instead of polling for changes, register a webhook URL and RxScale will send you a POST request whenever an event occurs.

How Webhooks Work

  1. Register a webhook subscription with your target URL and desired event type.
  2. Receive POST requests to your URL when events occur.
  3. Verify the webhook signature to ensure the request is authentic.
  4. Respond with a 2xx status code to acknowledge receipt.

Webhook Payload Format

All webhook payloads follow the same envelope structure:

Processing Events Idempotently

Pharmacy order and stock events (pharmacy_order_created, pharmacy_order_updated, pharmacy_sku_stock_updated) do not describe a change. Each payload is a complete snapshot of the pharmacy order or pharmacy SKU, built at the moment the delivery is sent. There is no event ID. As a result:
  • The same state can reach you more than once — for example when a delivery is retried, or when several changes happen in quick succession.
  • pharmacy_order_updated can arrive before the pharmacy_order_created of the same order — for example when the first delivery of pharmacy_order_created failed and is retried later.
  • pharmacy_order_updated does not say what changed.
Build your processing around these rules:
  1. Use data.uid as the key and upsert: create the record if you don’t know it yet, whichever event arrives first.
  2. Treat every payload as the latest full state and replace your stored copy with it. To find out what changed, compare it with what you stored — for example status or shipments.
  3. Skip a payload whose data.updated_at is older than the value you stored. It is an older snapshot that arrived late.
  4. Make sure that processing the same payload twice is harmless.
  5. Do not deduplicate on timestamp. It is set per delivery attempt: a retry carries a new value, while two different updates sent in the same second share one.
appointment_reminder_due and patient_doctor_meeting_updated carry the data captured when the event occurred, not a snapshot built at delivery time. For meeting events, see Delivery and Idempotency.

Registering Webhooks

You can register webhooks through:
  • Pharmacy Portal — Navigate to API Access in the sidebar. If you manage multiple pharmacy groups, select the group from the dropdown at the top of the page.
  • External Pharmacy API — POST /v1/external_pharmacy_api/webhooks/
  • Management API — POST /v1/management/notification-subscriptions/
Every registration returns a webhook_secret in the creation response, whether you register through the External Pharmacy API, the Pharmacy Portal or the Management API. It is shown only once, so store it securely: you need it to verify payload signatures (see Security). Management API subscriptions can additionally send a custom request header you configure (header_key / header_value), for example an API key your endpoint already checks.

Retry Policy

Successful and Failed Deliveries

A delivery counts as successful as soon as your endpoint responds with any 2xx status code — even if your own processing of the payload fails afterwards. Store the payload first, respond, and do heavy work asynchronously. A delivery counts as failed when:
  • your endpoint responds with any other status code — including 3xx (redirects are not followed) and every 4xx, such as 413 Payload Too Large;
  • your endpoint does not respond within 30 seconds;
  • the connection cannot be established or the TLS handshake fails (for example because of an invalid certificate).
If your target URL resolves to a private or internal IP address at the time of delivery, the delivery is refused and not retried.

Retries

Failed deliveries are retried with exponential backoff: the first retry follows after roughly 10 seconds, and the wait grows to at most about 10 minutes between attempts. After 5 attempts in total, the delivery is dropped. There is no dead-letter queue and no way to replay a dropped delivery. Each attempt builds the payload again, so a retry carries the current state and a new timestamp. If deliveries were dropped — for example after an outage of your endpoint — reconcile through the External Pharmacy API:
  • List orders with GET /v1/external_pharmacy_api/pharmacy_orders/. The start_date and end_date filters apply to the order’s creation time.
  • Fetch the current state of an order with GET /v1/external_pharmacy_api/pharmacy_orders/{pharmacy_order_uid}. The response is built from the same data as the webhook payload.
  • For stock levels, list your SKUs with GET /v1/external_pharmacy_api/pharmacy_skus/.
See Pharmacy Orders and Pharmacy SKUs for details. RxScale never disables a subscription because of failed deliveries. A subscription stays active until you delete it.

Delivery Logs

Every delivery attempt for the subscriptions of your pharmacy group — successful or failed — is listed in the Pharmacy Portal under API Access → Webhook Delivery Logs, with the response status code or the error. A dropped delivery is shown there as a permanent failure.

Payload Size

There is no fixed size limit, and payloads are never truncated. For subscriptions registered through the External Pharmacy API or the Pharmacy Portal, order payloads include the signed prescription PDF inline, Base64-encoded in data.prescription_file.content_base64 — never as a link. It is one PDF per order, sent in pharmacy_order_created and in every pharmacy_order_updated. prescription_file is null for orders that contain only over-the-counter products. Organisation-level deliveries (Management API) do not include prescription_file. Payloads are typically a few hundred kilobytes, but uploaded or externally signed prescription PDFs can be much larger. Configure your endpoint — and any proxy or framework in front of it — to accept request bodies of at least 10 MB. A 413 response is a failed delivery like any other: it is retried and then dropped.