Webhooks are HTTPS requests from one party — the source — to another party, the destination. These requests are one-way: the source sends the request to the destination, and aside from conforming that the request was received, takes no further action (the request’s response from the destination is irrelevant). These requests are sent from the source to let the destination know that something has happened: a new entity (or resource, in the REST sense) has been created, an old one updated or deleted, etc. Such requests typically carry a payload in the body providing information about the action (usually the representation of the affected resource). Destinations are identified via complete URL; destinations may expect to be informed of events affecting one, several, or all possible types of entities handled by the source.

This Package

This package is installed in a source server and manages the registration and sending of webhooks. The registrations may be either static, or they may be dynamic, as in the case of REST Hooks, where individual “subscriptions” may be started and stopped.

This package is intended to integrate with highly event-driven applications using zope.event, that define their resources using zope.interface, manage event delivery, resource adaptation, and dependency injection using zope.component, and (optionally) implement a hierarchy of component registries using zope.site and nti.site. Data persistence is provided through persistent objects, typically with ZODB.

Data Model (Subscription Combinations)

One of the motivating examples of this package is integration with Zapier and more generally the notion of REST Hooks.

In this model, a configuration on a server (origin) that sends data to a target URL when events occur is called a subscription. Subscriptions are meant to include:

  1. An event name (or names) the subscription includes;
  2. A parent user or account relationship;
  3. A target URL; and
  4. Active vs inactive state.

Subscription lookup must be performant, so the user and event name information for subscriptions should be fast to find.

Here, event names are defined to “use the noun.verb dot syntax, IE: contact.create or lead.delete).” Using zope.event and zope.component, this translates to the pair of object type or interface, and event type or interface. For example, (IContentContainer, IObjectAddedEvent).

Zapier generates a unique target URL for each event name, so to get created (added), modified, and deleted resources for a single type of object there will be three different target URLs and thus three different subscriptions. In general, there’s an N x M expansion of object types and event types to target URLs or subscriptions.

This package implements this model directly. (You can of course use umbrella interfaces applied to multiple object or event types to send related events to a single subscription.) Aggregating data views of “all webhook deliveries for a type of object” or “all webhook deliveries for a type of event” for presentation purposes could be written, but isn’t particularly natural given how its set up now.

An important outcome of this model is that there’s no need for any given HTTP request to explicitly include something that identifies the type of event; the default dialect (see below) assume that the URL includes everything the receiver needs for that and doesn’t do anything like add an X-NTI-EventType header or add something to the JSON body. It can be a URL parameter or a whole different URL, doesn’t matter.


See the Glossary for common terminology.



Write events document. Add specific events for subscription in/activated.


The concept of an active, in-process, retry policy.


An API to retry a failed request.

Thoughts on HTTP API


Generic end-point with context IPossibleWebhookPayload; the last part of the path (or a query param?) would be a shortcut name for the event.


Getting the attempts for a subscription should be easy. Have an endpoint for the subscription, just externalize.

Indices and tables