🕸 Building webhooks

We’ve been asked by a number of our customers to build webhooks, so that they can build their own services on top of Zeplo. Given that webhooks are a primary use case for Zeplo - it really makes sense to implement our own. Hopefully we can use Zeplo to power Zeplo’s own webhooks.

Webhooks Screenshot

Live Plan

Before getting started on any new feature, I always create a new doc with the following headings. I fill it out as much as possible, but as new things come up they get added - it’s a living document, you can’t predict everything ahead of time!

  • Why - why do we need this new feature? what was the use case or customer request that is driving this
  • Questions - these are unknowns which we will need to figure out. I usually write out a few possible options, before reviewing and turning them into decisions. This is great when returning to understand WHY you made a certain decision.
  • Requirements / decisions - once a question has been answered, I move the output here for clarity
  • Actions - the individual steps (design, development, etc) that are required to build this feature (this usually comes a little later in the flow)


  • Customers want to use Zeplo in their customer facing apps, and therefore need to show the user the current state of messages in the queue
  • Customers want to listen for failed events (e.g. dead letter queue) so they can handle/alert in the case that the message will never be delivered again


  • What events should we trigger for?

    • ❌ request.create request.update - I think it’s better to be more explicit
    • ❌ request.create request.update request.attempt
    • ✅ request.create request.update request.success request.error request.failed request.activate request.deactivate
  • Should we log every event regardless of whether a webhook is listening? E.g. create an event resource type that serves as an audit for all activity. This could be useful anyway in the requests page to see a history of what happened to the request

    • ✅ Yes - provides a great audit history which provides value to the request view - any pending request can be updated
    • ❌ No - most of the information is available in the request and some of it can’t be changed anyway.
  • Should request.update be for user based updates only OR for all updates? Should we differentiate between these?

    • ❌ Provide a reason type field which details why the change occurred
    • ✅ We could use request.update only for user updates, and then create separate events such as request.success or `request.
  • For any given underlying event, should we allow more than one hook event to be created - for example when a request errors we publish request.error but on the last error when there are no more retries remaining we might want to send both request.error and request.failed at the same time - both with the same data/state:

    • ✅ Yes - allow the same underlying action/event to trigger multiple hook events - this makes more sense from a users perspective. For example, a user may only be interested in request.failed not request.error
    • ❌ No - it could be confusing to receive the same data multiple times, especially if the user chooses to receive all events - in that case we would have the following options - also all of the required data for error/failed is available in the record update
  • Should we re-use the request model for our own webhook events?

    • ✅ Yes - we could create a new source WEBHOOK that is created for sending out webhook events (seems kind of beautiful)
    • ❌ No - could be confusing to re-use it, we might have to add a filter to the request - although this could easily be handled by adding a filter

Requirements / Decisions

  • User can create and edit a webhook
  • User can easily setup dead letter webhook
  • User can request:
  • Send me everything
  • Specific events
  • User can retry sending a webhook
  • Webhooks are signed, webhook key is provided to the user
  • Webhooks will be delivered as JSON only
  • Webhook events: request.create request.update request.success request.error request.failed request.activate request.deactivate

Out of scope

  • Local testing of webhooks via the CLI (although we hope to add this soon)
  • Filters - no ability to add filters or aggregate



Update docs (and add link)
Deploy! 🎉

Return to Blog