Webhooks

Webhooks allows third-party users to act on various events triggered by risr/advance. The third-party is responsible for creating a public http server which can accept the payload and for registering the required URLs.

In risr/advance an API user with a “Allow user to manage web hooks” permission can request to be notifed when certain actions happen. The request is called a registration where the user specifies on which action a given URL should be called. For example user can request so that a user profile will be send to a registered url when the profile is updated.

To simplify the process there is no authentication on the targets URL. Instead the payload is signed with a secret provided at registration so that the source is trusted.

Registration

Below there is an example of a registration for profile_updated hook.

>>> payload = {
    'topic': 'profile_updated,
    'url': 'https://myhostname/profile_updated',
    'secret': 'signature_secret'
}
>>> registration = requests.post('https://api.kaizenep.com/v2/webhooks/registrations', json=payload, headers=headers)
>>> response.json()
{"id": "new_registration_id", "rev": "revision number"}

When the above runs successfuly risr/advance will issue a POST request with a signed profile json to the url https://myhostname/profile_updated

Target destination

The webserver accepting the POST request can be implemented in any way and is fully in hand of the third party integrator. However there is an example server that can receive and parse the response.

Note

The example below uses Python and aiohttp (https://docs.aiohttp.org/en/stable/) and it is just to demonstrate how the payload is decoded. It is not a production ready solution.

import json
import jose.jws
from aiohttp import web

async def handle_profile_updated(request):
    # await the request payload
    data = await request.text()

    # Verify it has been signed by the secret provided at registration
    payload_str = jose.jws.verify(data, 'signature_secret', algorithms=['HS256'])

    payload = json.loads(payload_str.decode('utf-8'))

    # The actual handling of the payload
    print("The received payload is:")
    print(json.dumps(js, indent=2))

    return web.json_response({'ok': True})

app = web.Application()
app.add_routes([web.post('/profile_updated', handle_profile_updated)]),

if __name__ == '__main__':
    web.run_app(app, port=6785)

When the application is deployed and accesible at https://myhostname it is ready for receiving webhooks.

Testing webhooks

A convenient way to check the target application works well is to trigger a testing hook:

>>> data = {
        'topic': 'profile_uploaded
    }
>>> response = requests.post('https://api.kaizenep.com/v2/webhooks/test', json=data, headers=headers)

This code will trigger a profile_updated hook with an example payload. The application from above will print:

{
    "auditLog": [],
    "id": "profile_org_id",
    "webhook_request_id": "f47bf223-f5dd-4f58-aa05-28aaa234aa75",
    "email": "john@doe.com",
    "rev": "1-1324",
    "firstName": "John",
    "lastName": "Doe",
    "userFields": []
}

Available topics

  • user_logged_in

  • user_registered

  • profile_updated

  • booking_updated

  • booking_state_changed

user_logged_in

The user logged in hook is triggered after a successful log in before user is redirected back to the application. The hook is synchronous hence the user is blocked until all the hooks return a value. It is recommended to have as fast response time as possible. The reason for synchronous call is that the user sees changes immediatlely or the permissions are correctly set up before the application loads.

If a longer running action is required we recommend using a queue mechanism and handle the task asynchronously within the integration.

The payload contains kaizen user id and all the data that can be collected from SSO headers.

user_registered

When you accesses risr/advance for the first time the account is locally set up and user registered hook is called so that the profile can be updated from an external source so that the user has profile data and permission set up before they access the system. The hook is synchronous hence the user is blocked until all the hooks return a value.

The payload contains kaizen user id and all the data that can be collected from SSO headers.

profile_updated

Whenever a user profile is updated or edited this hook is called with the full new updated profile. This hook is asynchronous however we would still recommend to aim for a fast response.

booking_updated

Booking updated hook is triggered any time a booking is saved even if there are no changes to it. The payload contains the newly updated booking document.

booking_state_changed

Booking state changed is triggered when a transition is executed. It can be any transition even if the workflow state does not change. However some actions could be processed. The payload contains the new booking document plus information about the transition and processed actions.