Skip to main content Link Menu Expand (external link) Copy Copied

Using Ons API Webhooks in your integration

Ons API Webhooks allow external integrations to receive notifications of events occurring in the Ons Suite.

Usecases

There are two common usecases in building an integration that can be improved by using Ons API Webhooks:

Maintaining a synchronized dataset for use in your integration

After fetching all data once (using, for example, /t/employees/x-stream-connect/data), you can then stay up-to-date by subscribing to all event types for Employee and processing them as they come in.
Without webhooks, you would have to regularly poll the /t/employees/x-stream-connect/updates and /t/employees/x-stream-connect/deletes endpoints, and even when polling at 5 minute intervals you’d still always be slightly behind.

Responding to events within the Ons Suite.

For example, the next step after an employee creates a Client in Ons might be to perform some action in your integrated system, but this can only be done once that Client is synchronized.

  • Without webhooks, that user will need to wait until the next scheduled synchronization or trigger a manual sync. This is a poor user experience.
  • With webhooks, the Client can be synchronized to your integrated system by the time the User has switched.

How to use webhooks

Subscribing

In order to subscribe to webhooks, there are four steps to complete:

  1. First, generate a secret on the page of your connector (see Image 1). This secret will be used to verify the Hash-based Message Authentication Code (HMAC). How to implement this is described further in authentication
  2. Make sure that there is a POST endpoint, for receiving notifications, available on the webhook URL(s) you will use. This endpoint should respond with a 200 OK statuscode in case of a correct HMAC, and a 401 UNAUTHORIZED in case of an incorrect HMAC. To verify this implementation on your endpoint, our service will send 2 NOP events when configuring your URL. One event has a correct HMAC, and one has an incorrect HMAC (see NOP-event).
  3. In Ons API Dashboard, configure webhooks URLs for your integration. You need three URLs, one each for: development, staging, and production. All versions of your connector will make use of these URLs. Any more specific routing will need to be done on the integration side. Webhooks-url configuration Image 1.

  4. In the version that requires webhook subscriptions, select all combinations of Model and event-types that are of interest to the integration and then save. Your selected webhook subscriptions will be part of the review that is done before promotion to the next stage. Selection element for webhook subscriptions Image 2.

Due to redundancy and caching in our webhook service, it may take up to 5 minutes after saving before you reliably start (or stop) receiving events. You will only receive events from customer environments that have an active certificate.

Handling notifications

The Notification-objects posted to your notification endpoint will always be thin notifications. This means that they will only contain enough information to tell you which customer environment is involved, which type of record changed in what way and what the id of this record is, see notification model.
Therefore, your integration will always need access to APIs that allow fetching these records by their ID so your integration can fetch them, inspect them and determine what to do.

Technical details and examples

Webhook endpoint

The Ons API Webhook service will POST Notification-objects to the configured notification endpoint. Your integration should accept these requests and respond with a 200 status code to signal the notification was received. Requests where no connection can be made, or that do not receive a 200 response within 5 seconds will be considered failed requests. We suggest you accept requests, store the notification in some sort of queue, respond with a 200 status code and then handle the request in a different process. This decouples receiving and handling events and prevents you from missing events when handling slows down temporarily. Plan to receive 100s events / second as traffic may peak in case of: bulk corrections, imports, on-demand bulk redelivery of failed notifications, or catch-up after downtime.

Authentication

  • Our service will authenticate itself by use of an HTTP-header named X-Signature-SHA512. This header is an HMAC (Hash-based message authentication code) that is generated by hashing the secret with the request payload using the SHA512 algorithm. The secret can be generated in the page of the connector.
  • In case of a correct HMAC our service expects a response with a 200 OK statuscode. In case of an incorrect HMAC our service expects a response with a 401 UNAUTHORIZED statuscode. When configuring your URL, our service will verify this implementation with a NOP-event
  • We expect your server to use HTTPS.

This is how the HMAC can be generated in Java:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class SignatureGenerator {

    private static final String ALGORITHM = "HmacSHA512";

    public static String generateSignature(String data, String secret) throws NoSuchAlgorithmException, InvalidKeyException {
        Mac mac = Mac.getInstance(ALGORITHM);

        SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(), ALGORITHM);

        mac.init(secretKeySpec);

        byte[] macBytes = mac.doFinal(data.getBytes());
        
        return toHexString(macBytes);
    }

    private static String toHexString(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = String.format("%02x", b);
            hexString.append(hex);
        }
        return hexString.toString();
    }

    public static void main(String[] args) {
        try {
            String data = "{\"customerCode\":\"TE1000\",\"modelType\":\"client\",\"eventType\":\"CREATE\",\"id\":1,\"timestamp\":\"2024-08-22T10:08:11+02:00\",\"amountOfRetries\":0}";
            String secret = "SuperSecret";
            String signature = generateSignature(data, secret);
            System.out.println("Signature: " + signature);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }
    }
}

Notification model

The Notification’s sent will look like this:

{
  "customerCode": "TE1002",
  "modelType": "client",
  "eventType": "CREATE",
  "id": 1234,
  "timestamp": "2024-08-22T10:08:11+02:00",
  "amountOfRetries": 0
}
  • customerCode will identify the customer environment and this matches the customerCode used in the certificates you use for API requests.
  • modelType will indicate the model.
  • eventType the type of action (CREATE, UPDATE, DELETE, CUSTOM, NOP) that triggered the event.
  • id the id of the model.
  • timestamp when the event was triggered.
  • amountOfRetries how often the event has been tried to deliver.

Available modelType’s en eventType’s will be expanded over time, but you will never receive events you are not subscribed on.

NOP event

A NOP event is short for “no operation” event. When receiving a NOP event, only the HMAC has to be verified. Respond with either a 200 or 401 status code based on the HMAC validation result. NOP events can be ignored (they are only used to validate your HMAC implementation).

Supported Events

CREATE, UPDATE, DELETE Events
EventType ModelType Description
client CREATE, UPDATE, DELETE When creating, updating or deleting a client.
client_address CREATE, UPDATE, DELETE When creating, updating or deleting a coupling between a client and address.
client_contact_relation CREATE, UPDATE, DELETE When creating, updating or deleting a relation between a client and contact.
insurance CREATE, UPDATE, DELETE When creating, updating or deleting an insurance.
client_contact_relation_address CREATE, UPDATE, DELETE When creating, updating or deleting an address of a contact that has a relation with a client.
location_assignment UPDATE When updating a coupling between a location and client.
report CREATE, UPDATE, DELETE When creating, updating or deleting a report.
employee CREATE, UPDATE, DELETE When creating, updating or deleting an employee.
hour_type CREATE, UPDATE When creating or updating an hour type.
location CREATE, UPDATE, DELETE When creating, updating or deleting a location.
team CREATE, UPDATE, DELETE When creating, updating or deleting a team.
user CREATE, UPDATE, DELETE When creating, updating or deleting a user.
address CREATE, UPDATE, DELETE When creating, updating or deleting an address.
employee_address CREATE, UPDATE, DELETE When creating, updating or deleting a coupling between an employee and address.
expertise_profile CREATE, UPDATE When creating or updating an expertise profile.
contract CREATE, UPDATE, DELETE When creating, updating or deleting a contract of an employee.
care_plan CREATE, UPDATE, DELETE When creating, updating or deleting a care plan.
team_assignment CREATE, UPDATE, DELETE When creating, updating or deleting a coupling between an employee and team.
care_allocation CREATE, UPDATE, DELETE When creating, updating or deleting the care allocation of a client.
client_employee_relation CREATE, UPDATE, DELETE When creating, updating or deleting a relation between a client and employee.
CUSTOM Events

We also support several events that are not a CREATE/UPDATE/DELETE of a single record but refer to a specific action. This table highlights these events and explains which records the ID field in the notification refers to:

EventType ModelType Description ID refers to
external_care_providers_changed CUSTOM When changing the care providers of a client Client
client_employee_relations_changed CUSTOM When changing the employee relations of a client. Client
team_assignment_changed CUSTOM When changing the team of an employee. Employee
care_plan_activated CUSTOM When activating the care plan of a client. Client
client_careallocations_changed CUSTOM When changing the care allocation of a client. Client

Delivery order

We can not guarantee notifications are delivered in the order they are triggered. This means that you may see an UPDATE notification for a client for which you have not seen the CREATE yet. Design your integration to be able to handle situations like this. This can occur because our webhook service is deployed with multiple instances that prefetch notifications in batches, and because delivery for a single notification may fail and the retry might place it after another notification for the same record for which delivery succeeded. For example:

10:00:00 CREATE CLIENT 1  <- Delivery of this notification fails
10:00:05 UPDATE CLIENT 1  <- Delivery of this notification succeeds
10:01:00 CREATE CLIENT 1  <- Re-delivery of the notification succeeds this time

Failed delivery attempts

Requests that are not answered with a 200 status code within 5 seconds will be considered failed requests. Delivery will not be retried automatically. We will store events for which delivery has failed for 24 hours and then discard them. We can store up to 1.000.000 events, after that events will start to overflow and will be lost. Your integration can request re-delivery of failed notifications by sending a PUT request with empty body to /webhooks/redeliver. This can be done with a certificate that belongs to a connector, with this you can trigger a retry per stage (development, staging, production) for all missed notifications for your connector.

Plan for resynchronization

Failed deliveries of Ons API Webhook notifications will be stored for 24 hours and are available for retry during that period. If your webhook endpoint is down for longer than that period, the missed notifications are lost. We suggest you design an option for resynchronization in case that happens.

Event loops

If you respond to a notification by updating the record in Ons that you received the notification for, you’ll receive another event for that same record. If that second event triggers another update from your integration, you may cause an event loop. Examples of two situations causing an event loop below.

Example of an event loop with a single integration

As an example of how things can go wrong, assume you’ve implemented an automatic spelling corrector that responds to notifications about the creation or update of a Report and saves the Report again with corrected spelling. What can happen if you save the Report without checking whether anything changed:

  1. Event received for Report 1234
  2. Fetch Report 1234
  3. Fix spelling
  4. PUT Report 1234 (regardless of whether anything changed about the model)
  5. Return to 1

You can break this loop by detecting that your integration changed nothing about the Report and not saving it.

With multiple integrations

Building on the example above. Even if your integration is aware of notifications created by its own actions, two integrations may trigger each other. Imagine two spell checker integrations, that do not cause event loops on their own, with slightly different spelling rules:

  1. Both integrations receive CREATE event for Report 1234
  2. Both integrations do their spelling check
    1. Integration A makes no changes and does not update the Report
    2. Integration B makes some changes and updates the Report
  3. Both integrations receive UPDATE event for Report 1234
    1. Integration A makes changes, and updates the Report
    2. Integration B makes no changes and does not update the Report
  4. Both integrations receive UPDATE event for Report 1234
    1. Integration A makes no changes and does not update the Report
    2. Integration B makes changes and updates the Report
  5. Return to 3

This loop is a bit harder to break, since something has changed since last time your integration saved the record. We suggest implementing a check to make sure that you’re not repeatedly editing the same record.

Open API specification

Your webhooks endpoint should conform to this specification. You can configure your own path. Download the raw specification here.