Hi John, thanks for the swift reply!
I’m happy to elaborate. We at bardeen.ai are building a platform where users can connect various services like Zoom, Asana, and others, and generate automations between these.
Since we integrate many services that do webhooks, our webhooks infrastructure is quite generic in that we don’t create webhooks for us, but on behalf of users who use our platform to build automations. When a user creates an automation that triggers “when a Task is created in Asana”, we register webhook notfications using the user’s token against our server.
There are two main groups of services that send webhooks:
(1) Zoom or HubSpot only allow a single, hardcoded URL, and will send domain specific user account information in “rich” events to (I’m simplifying here) /webhooks/<SID>/
. We validate authenticity of received events, and forward as needed. These services btw. will send events for any user who integrated e.g. Zoom with Bardeen, so we need a mechanism to tell apart relevant events from irrelevant ones (i.e.: does the user with whom this event is associated have an automation enabled that needs to run?).
(2) Services like Google or Asana allow us to register dynamic target URLs – and usually don’t send any information in the payload that would allow us to associate an event with a user in order to check that we expect that event and need to process it.
On top of these differences, we need a way to register and deregister webhook notifications based on the existence and absence, respectively, of automations: we “subscribe” to certain events when an automation is enabled, and unsubscribe when no automation requires these events any longer.
In order to handle these requirements, our infrastructure tracks what we call subscriptions
. A subscription
has a channel
and subject
, and is created in our system when a user activates an automation.
For cases like Zoom or HubSpot, we have custom route handlers which inspect the payload and will derive the channel based on the given information. For services that allow dynamic target URLs, we encode channel information into the URL. When we receive an event against a URL that was dynamically registered, we derive the channel from that URL, which is the case for Asana. All of these route handlers will forward the received event along with channel information, and a step later in that pipeline will discard events for whose channels no subscription exists.
So, in the example provided we have webhooks/json/asana/<channel>
. Since the channel must allow to encode arbitrary information, it contains separators and special character of all sorts, and we url-encode the path segments in order to have a valid URL.
I hope this clarifies why we’re doing what we’re doing. We could have used any other url safe encoding for channels, but went with url-encoding since it seemed the most obvious and is shortest.