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.
- 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
POSTrequests and can respond quickly (e.g., return 2xx within a few seconds) so SNS does not retry unnecessarily.
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/webhookFor local development, use a tunnel (e.g., ngrok) so SNS can reach your machine:
https://abc123.ngrok.io/webhookRegister 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.
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:
- Parse the incoming POST body as JSON.
- If
Type === "SubscriptionConfirmation", call theSubscribeURL(e.g., with an HTTP GET). - Return 2xx to SNS.
If you do not confirm, the subscription stays pending and no data change notifications will be delivered.
StreamAPI webhooks use the standard AWS SNS message envelope. The body of the POST is a single JSON object.
| Field | Description |
|---|---|
| Type | Notification, SubscriptionConfirmation, or UnsubscribeConfirmation |
| MessageId | Unique ID for the message (use for idempotency). |
| TopicArn | SNS topic ARN (e.g., arn:aws:sns:us-east-1:…:Org_<orgId>). |
| Message | For Type === "Notification", a string containing a JSON object with the StreamAPI event. |
| Timestamp | ISO 8601 timestamp when SNS sent the message. |
| SignatureVersion | Signature version (e.g., 1). |
| Signature | Base64-encoded signature for verification. |
| SigningCertURL | HTTPS URL to the AWS certificate used to sign the message. |
| UnsubscribeURL | URL to unsubscribe (for Notification). |
| MessageAttributes | Optional attributes (e.g., RoutingKey, organizationId, MessageType). |
For SubscriptionConfirmation, the body also includes:
- SubscribeURL – URL to call to confirm.
- Token – Subscription token.
When Type is Notification, the Message field is a stringified JSON object. Parse it to get the StreamAPI payload:
| Field | Description |
|---|---|
| messageType | e.g., DataChange. |
| applicationId | Application identifier. |
| clientName | Client name (e.g., Developer). |
| type | Entity type (e.g., OperatoryV1, PatientV1). |
| id | Entity ID. |
| operation | CREATE, UPDATE, or DELETE. |
| payload | Entity payload (varies by type). |
| transactionId | Transaction ID. |
| correlationId | Correlation ID for tracing. |
| sendTime | Epoch milliseconds when the event was sent. |
MessageAttributes on the SNS envelope often include:
| Attribute | Description |
|---|---|
| RoutingKey | Format: {organizationId}.{locationId}.{EntityType}.{OPERATION} (e.g., 5d111c20ca7b487854a79d0d.-1.OperatoryV1.UPDATE). |
| organizationId / OrganizationId | Organization ID. |
| locationId | Location ID (-1 if org-level). |
| MessageType | e.g., StreamAPI-Internal. |
You can use RoutingKey or MessageAttributes to filter or route events without parsing the full Message body.
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
}To ensure messages are from AWS SNS and have not been altered, verify the Signature using the certificate at SigningCertURL.
- Validate SigningCertURL – Allow only URLs from
https://sns.<region>.amazonaws.com/(orhttps://sns.amazonaws.com/). Reject any other host. - Download the certificate – Fetch the PEM from
SigningCertURLover HTTPS (cache it by URL for a short TTL to avoid repeated downloads). - 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. ForSubscriptionConfirmationandUnsubscribeConfirmation, use the field set that includesSubscribeURLorUnsubscribeURLandToken. Follow AWS SNS Verify Signature. - 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.
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.
| Step | Action |
|---|---|
| 1 | Expose an HTTPS endpoint that accepts POST and returns 2xx. |
| 2 | Register that URL as your StreamAPI webhook in the platform. |
| 3 | On first request, if Type === "SubscriptionConfirmation", call SubscribeURL to confirm. |
| 4 | For Type === "Notification", parse Message as JSON and process the StreamAPI event. |
| 5 | Verify the SNS signature using SigningCertURL and reject invalid messages. |
| 6 | Use 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.