Skip to content

Overview

StreamAPI webhooks push real-time data change events to your application when entities (e.g., operatories, patients, appointments) are created, updated, or deleted. Notifications are delivered over Amazon SNS (Simple Notification Service). Your server receives an HTTP POST for each event and must handle both subscription confirmation and notification payloads.

This guide explains how to create and set up webhooks specifically for the StreamAPI: registering your endpoint, understanding the payload structure, confirming subscriptions, and verifying message signatures.


Prerequisites

  • StreamAPI access – Your application must be authorized to use the StreamAPI (see Obtaining Production Access if applicable).
  • Public endpoint – Your webhook URL must be reachable from the internet over HTTPS. AWS SNS cannot deliver to private IPs or localhost unless you use a tunnel (e.g., ngrok) for development.
  • HTTP server – A server that accepts POST requests and can respond quickly (e.g., return 2xx within a few seconds) so SNS does not retry unnecessarily.

Creating and Registering Your Webhook

1. Expose a webhook endpoint

Your application must expose an HTTP endpoint that:

  • Accepts POST requests.
  • Accepts a JSON or text/plain body (SNS may send JSON as text/plain).
  • Returns a 2xx status code after processing (or immediately after receiving) the request so SNS considers the delivery successful.

Example URL form:

https://your-domain.com/webhook

For local development, use a tunnel (e.g., ngrok) so SNS can reach your machine:

https://abc123.ngrok.io/webhook

2. Register the endpoint with the platform

Register your webhook URL with the StreamAPI by submitting a support ticket.

  • Provide the full webhook URL (HTTPS).

After registration, the platform creates an SNS subscription for your endpoint.

3. Confirm the subscription

When you first register, AWS SNS sends a SubscriptionConfirmation message to your URL. You must confirm the subscription or you will not receive future notifications.

  • Type: SubscriptionConfirmation
  • SubscribeURL: A one-time URL you must GET (or POST) from your server. When SNS receives this request, the subscription is confirmed.
  • Your endpoint should:
    1. Parse the incoming POST body as JSON.
    2. If Type === "SubscriptionConfirmation", call the SubscribeURL (e.g., with an HTTP GET).
    3. Return 2xx to SNS.

If you do not confirm, the subscription stays pending and no data change notifications will be delivered.


Payload Structure

StreamAPI webhooks use the standard AWS SNS message envelope. The body of the POST is a single JSON object.

Top-level SNS envelope

FieldDescription
TypeNotification, SubscriptionConfirmation, or UnsubscribeConfirmation
MessageIdUnique ID for the message (use for idempotency).
TopicArnSNS topic ARN (e.g., arn:aws:sns:us-east-1:…:Org_<orgId>).
MessageFor Type === "Notification", a string containing a JSON object with the StreamAPI event.
TimestampISO 8601 timestamp when SNS sent the message.
SignatureVersionSignature version (e.g., 1).
SignatureBase64-encoded signature for verification.
SigningCertURLHTTPS URL to the AWS certificate used to sign the message.
UnsubscribeURLURL to unsubscribe (for Notification).
MessageAttributesOptional attributes (e.g., RoutingKey, organizationId, MessageType).

For SubscriptionConfirmation, the body also includes:

  • SubscribeURL – URL to call to confirm.
  • Token – Subscription token.

Inner message (StreamAPI event)

When Type is Notification, the Message field is a stringified JSON object. Parse it to get the StreamAPI payload:

FieldDescription
messageTypee.g., DataChange.
applicationIdApplication identifier.
clientNameClient name (e.g., Developer).
typeEntity type (e.g., OperatoryV1, PatientV1).
idEntity ID.
operationCREATE, UPDATE, or DELETE.
payloadEntity payload (varies by type).
transactionIdTransaction ID.
correlationIdCorrelation ID for tracing.
sendTimeEpoch milliseconds when the event was sent.

MessageAttributes (StreamAPI)

MessageAttributes on the SNS envelope often include:

AttributeDescription
RoutingKeyFormat: {organizationId}.{locationId}.{EntityType}.{OPERATION} (e.g., 5d111c20ca7b487854a79d0d.-1.OperatoryV1.UPDATE).
organizationId / OrganizationIdOrganization ID.
locationIdLocation ID (-1 if org-level).
MessageTypee.g., StreamAPI-Internal.

You can use RoutingKey or MessageAttributes to filter or route events without parsing the full Message body.


Example notification payload

Below is an example of a Notification payload for a StreamAPI data change event.

{
  "Type": "Notification",
  "MessageId": "1899eea0-81b3-5112-88dd-8cae7f6399b5",
  "TopicArn": "arn:aws:sns:us-east-1:393668382772:Org_5d111c20ca7b487854a79d0d",
  "Message": "{\"messageType\":\"DataChange\",\"applicationId\":\"hAnIzDLqlKBpPz2hscufe4HpuGmmkpl4\",\"clientName\":\"Developer\",\"type\":\"OperatoryV1\",\"id\":\"64000000000005\",\"operation\":\"UPDATE\",\"payload\":{\"shortName\":\"testop14\",\"id\":\"64000000000005\",\"type\":\"OperatoryV1\"},\"transactionId\":\"116c84a5-33ba-4d4c-b46c-3ebce5d506e0\",\"correlationId\":\"116c84a5-33ba-4d4c-b46c-3ebce5d506e0\",\"sendTime\":1771371664352}",
  "Timestamp": "2026-02-17T23:41:04.426Z",
  "SignatureVersion": "1",
  "Signature": "fANin6ysjQQknuP7tRSjvDrnWacHtcVrFleUWizAQ6lp1xMATOuVzDpnAEcTkAYdb7TqmeFUQXRP4DYOaRgQ6KMnvApgS1axLMI5nBEe/AdjFBeRR9GJwBNwX/0PcIDlySY+MMwU2eXg8V9ud+/RvkQPlECxm3CAJv+9ItdxXSd2jGqfQ+eVJyyLBhqnBXjwpyh7d5evwks3Dv9pB+4Mu717KpT9QAR/yCc3LXWUh4DgeCTlCZYS8xcgr0LgwVck2WIQnDBLeDkJgwYvNJLfh6Mwq2q+HaErDnccRygMsRky7Csfy41EypirEFZllSOIHAzK/oPSNtL/ehod3mlzDw==",
  "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-7506a1e35b36ef5a444dd1a8e7cc3ed8.pem",
  "UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:393668382772:Org_5d111c20ca7b487854a79d0d:f47e759a-a606-4302-82ba-f2b1bedd6618",
  "MessageAttributes": {
    "RoutingKey": {"Type":"String","Value":"5d111c20ca7b487854a79d0d.-1.OperatoryV1.UPDATE"},
    "organizationId": {"Type":"String","Value":"5d111c20ca7b487854a79d0d"},
    "locationId": {"Type":"String","Value":"-1"},
    "OrganizationId": {"Type":"String","Value":"5d111c20ca7b487854a79d0d"},
    "MessageType": {"Type":"String","Value":"StreamAPI-Internal"}
  }
}

Parsed Message (inner event):

{
  "messageType": "DataChange",
  "applicationId": "hAnIzDLqlKBpPz2hscufe4HpuGmmkpl4",
  "clientName": "Developer",
  "type": "OperatoryV1",
  "id": "64000000000005",
  "operation": "UPDATE",
  "payload": {
    "shortName": "testop14",
    "id": "64000000000005",
    "type": "OperatoryV1"
  },
  "transactionId": "116c84a5-33ba-4d4c-b46c-3ebce5d506e0",
  "correlationId": "116c84a5-33ba-4d4c-b46c-3ebce5d506e0",
  "sendTime": 1771371664352
}

Verifying message signatures

To ensure messages are from AWS SNS and have not been altered, verify the Signature using the certificate at SigningCertURL.

  1. Validate SigningCertURL – Allow only URLs from https://sns.<region>.amazonaws.com/ (or https://sns.amazonaws.com/). Reject any other host.
  2. Download the certificate – Fetch the PEM from SigningCertURL over HTTPS (cache it by URL for a short TTL to avoid repeated downloads).
  3. Build the string to sign – For Notification, AWS documents the canonical form using the fields: Message, MessageId, Subject, Timestamp, TopicArn, Type, in that order, with each key and value followed by a newline. For SubscriptionConfirmation and UnsubscribeConfirmation, use the field set that includes SubscribeURL or UnsubscribeURL and Token. Follow AWS SNS Verify Signature.
  4. Verify – Use the certificate’s public key and the algorithm (e.g., RSA-SHA1 for SignatureVersion 1) to verify the Signature (Base64-decoded) against the string to sign.

Reject any message that fails verification.


Handling duplicate deliveries

SNS may deliver the same notification more than once. Use MessageId (and optionally transactionId / correlationId inside Message) to deduplicate:

  • Store the last N MessageId values (or use a short-lived cache).
  • If you have already processed a given MessageId, return 2xx and skip processing again.

This keeps your handling idempotent and avoids duplicate side effects.


Summary checklist

StepAction
1Expose an HTTPS endpoint that accepts POST and returns 2xx.
2Register that URL as your StreamAPI webhook in the platform.
3On first request, if Type === "SubscriptionConfirmation", call SubscribeURL to confirm.
4For Type === "Notification", parse Message as JSON and process the StreamAPI event.
5Verify the SNS signature using SigningCertURL and reject invalid messages.
6Use MessageId (and optionally routing attributes) for idempotency and filtering.

For more on the StreamAPI and authentication, see the API Consumer Guide and API Best Practices.