Skip to content

Outbound Callbacks

Outbound callbacks (webhooks) let your own systems react when something happens on the platform. When one of these events occurs, the platform sends an HTTPS request to an address you chose:

  • Visit Started and Visit Closed
  • Patient Created (a patient was added to your organization) and Patient Updated (including when the patient accepts, declines or leaves your organization)
  • Patient Removed (your staff removed the patient from your organization, or the patient withdrew a request to join that you had not accepted)

Each such address, together with the event it listens for, is a subscription. This screen is where your organization adds, changes and removes its subscriptions, and sees how each delivery went. You can have at most one active subscription per event.

Outbound callbacks are part of the Integrations add-on (see Plans and add-ons). Without it the screen is not in the sidebar, opening it says that your plan doesn’t include it, and the API refuses its requests.

Each row shows the subscription’s target URL, its event, whether an authentication header is configured, when it was created, and whether it is Active. Events that happen while a subscription is inactive are not sent later. From a row’s menu you can:

  • View Deliveries to see the subscription’s delivery history.
  • Edit Subscription to change its target URL, or to set, replace or remove its authentication header. Pointing a paused subscription at a new URL lifts the pause straight away.
  • Delete Subscription to remove it, together with its delivery history and any deliveries still waiting to be sent. Deleting is permanent.

Use the Active switch to stop and resume deliveries without losing the subscription.

Choose Create Subscription and fill in:

  • Event Type: the event to be notified of.
  • Target URL: your endpoint. It must start with https:// and be reachable on the public internet; addresses inside private networks are refused.
  • Header Name and Header Value (optional): a fixed header, such as Authorization: Bearer …, sent with every request so your endpoint can authenticate the platform. Once saved, the value is never shown again. The name cannot be one the platform sets itself, such as Host, Content-Type or any X-Nightingale-… header. The value can be up to 2048 characters of plain text (letters, digits, punctuation and spaces), on a single line.

When the subscription is created you are shown its signing secret. Copy it then: it is not shown again. If you lose it, delete the subscription and create a new one to get a new secret.

The platform team can disable a subscription, for example when its endpoint keeps misbehaving. A disabled subscription is marked Disabled by the platform team in the list, with the date. While it is disabled:

  • nothing is delivered to it, and events that happen meanwhile are not sent later;
  • deliveries that were still waiting to be sent when it was disabled are cancelled. They show as dead-lettered, with the reason, in its delivery history, and are not sent when it is enabled again;
  • its switch, Edit Subscription and Delete Subscription are unavailable;
  • you cannot create or activate another subscription for the same event.

Contact support to have it enabled again. Once it is, it goes back to the state you had left it in.

Each callback is a POST with a JSON body:

{
"version": 1,
"event_type": "visit_closed",
"organization_id": "…",
"resource_id": "…",
"occurred_at": "2026-01-31T14:05:00Z",
"data": { "visit": { "…": "…" } }
}

occurred_at is when the event happened, which can be earlier than when you receive it. data is a snapshot of the visit or patient, and contains health information: treat your endpoint accordingly. A Patient Removed callback is the exception: your organization can no longer see the patient, so data holds only the patient’s identifier.

A Visit Closed callback carries the visit’s records only in the categories the patient shares with your organization (vitals, lifestyle, clinical), and none once the patient’s association with your organization has expired: the same records your staff can see on the patient’s screens. It also carries the patient’s whole family history, whenever it was recorded, since family history belongs to the patient rather than to a visit.

Every field of every event is listed in the Callbacks Reference, which is also available as an OpenAPI 3.1 document you can validate bodies against. In short:

  • Every listed field is always present. A field with no value is null.
  • Codes are sent rather than translated text. Catalog entries (diagnoses, drugs, lab analytes…) carry a stable code and their name in every language the catalog has.
  • Timestamps are in UTC, and decimal values are strings, so no precision is lost.
  • Within a version, fields and enum values may be added: ignore what you don’t recognise. Removing or changing a field is a new version, which your subscription only receives once it is moved to it.

Every request carries these headers:

Header Meaning
X-Nightingale-Signature t=<unix seconds>,v1=<signature>. See below.
X-Nightingale-Delivery-Id Unique per delivery. It is the same on every retry, so use it to ignore duplicates.
X-Nightingale-Event-Id Unique per event: the visit or patient change being reported. You have at most one active subscription per event type, so an event is delivered once; like the delivery ID, this is the same on every retry.

If your endpoint needs its own credential, give the subscription a fixed header, such as Authorization, and it is added to every request.

When the subscription was created you were shown its signing secret. To check that a request really came from the platform:

  1. Split X-Nightingale-Signature into its t and v1 parts.
  2. Compute the HMAC-SHA256 of <t>.<raw request body>, keyed with your signing secret, as lower-case hexadecimal. Use the body exactly as received, before any JSON parsing.
  3. Compare it with v1 using a constant-time comparison.
  4. Reject requests whose t is too far from your current time, for example more than five minutes, to stop replays.

Answer with any 2xx status within 15 seconds to confirm receipt. Do slow work after responding. Redirects are not followed: a 3xx answer counts as a failure. Anything else, including a timeout, counts as a failure and the delivery is retried: after 30 seconds, then with the delay doubling up to an hour between tries. After 10 attempts, about six hours, it is dead-lettered and not sent again.

If every recent delivery to a subscription has failed, the platform pauses that subscription for a few minutes, then tries again. Target URLs must use https://.

The address of your endpoint is looked up again for every attempt, and the platform only connects to public internet addresses. If your endpoint’s name comes to point at a private network address, attempts fail until it points at a public one again.

The screen needs the permission to view callbacks. Creating, changing, switching and deleting subscriptions needs the permission to manage callbacks; without it the screen is read-only. The default Administrator role of a newly created organization has both; in an existing organization, add the manage permission to a role on the Roles screen. No one can see signing secrets or authentication header values here, including after they are saved.

A subscription receives every patient’s events, so staff whose access is limited to patients with certain tags (a patient access restriction, see User details) cannot create a subscription or change one, even with the manage permission. They can still view and delete them.