stapel-webhooks
The reaction layer. Your modules already emit facts — user.registered,
listing.published, payment.completed — as schema-typed comm Actions
riding a transactional outbox. This is the consumer side: a declarative
"when X happens, deliver it to Y" that the app layer and external systems
can both use.
comm Action (already there)
│
▼ subscription registry + matcher
├─► webhook HTTP POST, HMAC-signed, retried, dead-lettered
├─► notification email/push/SMS via stapel-notifications
├─► ws live frame on a realtime stream
└─► custom allowlisted dotted path in your app
Part of the Stapel framework.
Install
pip install stapel-webhooks
INSTALLED_APPS = [..., "stapel_webhooks"]
path("webhooks/", include("stapel_webhooks.urls")) # -> /webhooks/api/v1/...
Then schedule the drain — with the default deferred dispatch, nothing is delivered until it runs:
from stapel_webhooks.tasks import get_webhooks_beat_schedule
CELERY_BEAT_SCHEDULE = {**get_webhooks_beat_schedule(), ...}
…or from cron: manage.py deliver_webhooks. (A system check tells you if you
forget.)
Subscribe to something
POST /webhooks/api/v1/subscriptions
{
"event_type": "listing.published",
"delivery": "webhook",
"target": {"url": "https://crm.example.com/hooks/stapel"},
"filter": {"city": "berlin", "price": {"$gte": 500}}
}
→ 201 {"id": "3fa8…", "secret": "whsec_…"}
The secret is returned once, on creation (and again only on rotation) — like an API key.
What is subscribable
Not a list somebody maintains: every event any installed module ships a
schema for, scanned out of schemas/emits/.
python manage.py webhooks_event_catalog
Install a module and its facts become subscribable. Uninstall it and they stop being offered. No release of this package either way.
What your receiver gets
X-Stapel-Delivery: 9aa1c0de-… ← identical across retries; de-duplicate on it
X-Stapel-Event: listing.published
X-Stapel-Signature: t=1755993600,v1=6f1e…c3
{"id":"9aa1…","type":"listing.published","created_at":"…","data":{ … }}
Verify: HMAC-SHA256(secret, f"{t}.{raw_body}"), compared in constant time
against the raw bytes — and refuse a t outside your tolerance window,
which is the replay guard. In Python, stapel_webhooks.verify(secret, body, header) does it for you and accepts a list of secrets so rotation has an
overlap.
When the receiver is down
Exponential backoff with a cap and jitter (8 attempts over ~2 h by default), then a dead letter — a row that keeps the payload and can be replayed from the API with the full ladder again. A 4xx that is not 408/425/429 skips the ladder entirely: eight identical refusals teach nobody anything. A rule that keeps dying deactivates itself and says so on the bus.
Closed by default
- webhook targets must be https and must resolve to a public address — private, loopback, link-local and the cloud metadata endpoint are refused, with no second DNS lookup between check and connect;
- delivery does not happen on your request thread;
- the
customdelivery type — a dotted path named by a database row — is unusable until you allowlist your handlers, and can be removed outright.
Each of those has a system check that reports it if you open it.
Documentation
MODULE.md— the integration contract: HTTP surface, comm surface, the delivery state machine, every extension point, and the known limitations.CONFIG.MD— every setting, and the four that are decisions rather than tuning.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stapel_webhooks-0.1.1.tar.gz.
File metadata
- Download URL: stapel_webhooks-0.1.1.tar.gz
- Upload date:
- Size: 88.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
99b118bd71c32588538b5c949d7293ba8a43f72d8724dda04f335855fa45133d
|
|
| MD5 |
7b79024b127dc6dbeac16ea73075cfcb
|
|
| BLAKE2b-256 |
2eab3bdb5d4044a517010b4e4d322e40e1a72ab407b2f9eb1447642d1c1e66dd
|
Provenance
The following attestation bundles were made for stapel_webhooks-0.1.1.tar.gz:
Publisher:
publish.yml on usestapel/stapel-webhooks
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_webhooks-0.1.1.tar.gz -
Subject digest:
99b118bd71c32588538b5c949d7293ba8a43f72d8724dda04f335855fa45133d - Sigstore transparency entry: 2619765127
- Sigstore integration time:
-
Permalink:
usestapel/stapel-webhooks@b47753ddbdadada83cc25f02a6a08f6c3bc0be1f -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b47753ddbdadada83cc25f02a6a08f6c3bc0be1f -
Trigger Event:
push
-
Statement type:
File details
Details for the file stapel_webhooks-0.1.1-py3-none-any.whl.
File metadata
- Download URL: stapel_webhooks-0.1.1-py3-none-any.whl
- Upload date:
- Size: 80.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
51b9aed9db72b579814e6dbc576fbf35bb8930bb2a788260d2ff0224fb1fe9c6
|
|
| MD5 |
1727b2eaaa154c5154cf5a792194ba21
|
|
| BLAKE2b-256 |
77ba785de62144e80d25ab9e024d38ddceff0fb1bda665045a40bdb62044eeb3
|
Provenance
The following attestation bundles were made for stapel_webhooks-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on usestapel/stapel-webhooks
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_webhooks-0.1.1-py3-none-any.whl -
Subject digest:
51b9aed9db72b579814e6dbc576fbf35bb8930bb2a788260d2ff0224fb1fe9c6 - Sigstore transparency entry: 2619765268
- Sigstore integration time:
-
Permalink:
usestapel/stapel-webhooks@b47753ddbdadada83cc25f02a6a08f6c3bc0be1f -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b47753ddbdadada83cc25f02a6a08f6c3bc0be1f -
Trigger Event:
push
-
Statement type: