Skip to main content

webhooks-cc

Python SDK for webhooks.cc: capture webhooks and email in your tests, wait for them, send signed webhooks to your own handlers, and verify provider signatures.

pip install webhooks-cc
pip install "webhooks-cc[crypto]"  # also verify Discord (Ed25519) and PayPal (RSA) signatures

Python 3.10 or later. Get an API key at webhooks.cc/account/api-keys and set WHK_API_KEY, or pass api_key=.

Wait for a webhook

from webhooks_cc import WebhooksCC, match_body_path

with WebhooksCC() as client:
    endpoint = client.endpoints.create(name="checkout test")

    trigger_checkout(webhook_url=endpoint.url)

    request = client.requests.wait_for(
        endpoint.slug,
        timeout="30s",
        match=match_body_path("type", "checkout.session.completed"),
    )
    assert request.json()["data"]["object"]["amount_total"] == 2000

    client.endpoints.delete(endpoint.slug)

Durations take "500ms", "30s", "5m", "2h", "7d", a timedelta, or a number of milliseconds (the unit every webhooks.cc SDK uses, so prefer a string where you mean seconds). Times such as since and Request.received_at are milliseconds since the epoch.

Matchers combine with match_all() and match_any(), and any function that takes a Request and returns a bool works too: match_method, match_path (globs with * and **), match_header, match_query_param, match_body_path, match_body_subset, match_content_type, match_verified, match_unverified and match_email.

pytest

Installing the package adds two fixtures. Tests that use them are skipped when WHK_API_KEY is not set.

def test_sends_a_receipt_webhook(webhooks_cc, webhooks_cc_endpoint):
    place_order(webhook_url=webhooks_cc_endpoint.url)

    request = webhooks_cc.requests.wait_for(webhooks_cc_endpoint.slug, timeout="30s")
    assert request.method == "POST"

webhooks_cc is a client for the session; webhooks_cc_endpoint is a new endpoint for each test, deleted afterwards. Outside pytest, temporary_endpoint(client) is the same as a context manager, and capture_during(client, action) runs action(endpoint) and returns what it sent.

Every endpoint receives email at {slug}@mailhooks.cc, and at {slug}+{tag}@mailhooks.cc for any tag. Use a fresh tag per test run to find your own email. webhooks.cc finds the one-time codes and links in each email, the same ones its dashboard shows.

import uuid
from webhooks_cc import extract_code, extract_link

run = f"signup-{uuid.uuid4().hex[:8]}"
sign_up(email=client.emails.address(endpoint, run))

email = client.emails.wait_for(endpoint.slug, tag=run, subject="Confirm your email")
code = extract_code(email)          # "482913"
link = extract_link(email)          # "https://app.example.com/confirm?token=..."

email.email holds the parsed message: subject, from_, to, text, html, attachments, and the SPF, DKIM and DMARC results.

Send signed webhooks to your handler

Test your own webhook handler, on localhost or anywhere else, with requests signed the way the provider signs them:

response = client.send_to(
    "http://localhost:8000/webhooks/stripe",
    provider="stripe",
    secret="whsec_test_secret",
    template="checkout.session.completed",
)
assert response.status_code == 200

Leave out body to send the provider's sample payload, or pass your own: a string or bytes go as they are, anything else as JSON. timestamp (seconds) and message_id make the signature repeatable. build_request() returns the method, URL, headers and body without sending. Redirects are not followed, so signed requests only ever reach the host you named. client.endpoints.send_template(slug, ...) sends the same to a webhooks.cc endpoint. TEMPLATE_PROVIDERS lists the 34 providers.

Verify signatures

from webhooks_cc import verify_signature

valid = verify_signature(
    "stripe",
    body=request.body,          # the exact bytes or text received
    headers=request.headers,    # any mapping; names match in any case
    secret="whsec_...",
)

Twilio, Square and HubSpot also need url; HubSpot also method. Discord takes public_key. One function per provider is in webhooks_cc.verify.

When webhooks.cc forwards requests or email to your server, check that they came from it with the endpoint's forwarding secret (client.forwarding.secret(slug)):

from webhooks_cc import WebhookVerificationError, verify_forwarded_email

try:
    event = verify_forwarded_email(body, headers, secret)
except WebhookVerificationError as error:
    return 400 if error.code == "invalid_payload" else 401
print(event["data"]["subject"])

verify_forwarded_request() checks a request forwarded as JSON, and verify_relayed_request() one relayed as it was received.

Stream requests as they arrive

for request in client.requests.subscribe(endpoint.slug, timeout="10m", reconnect=True):
    print(request.method, request.path)

Errors

API errors raise WebhooksCCError with a status_code, or one of its subclasses: UnauthorizedError (401), NotFoundError (404), RateLimitError (429, with retry_after) and TimeoutError, which is also Python's built-in TimeoutError. Network failures raise httpx's exceptions. Retries are off unless you pass retry=RetryOptions(max_attempts=3).

Same behaviour as the TypeScript SDK

This SDK passes the same conformance vectors as @webhooks-cc/sdk: signing, signature verification, forwarding signatures, matchers and durations. They live in packages/sdk/conformance.

License

MIT

Metadata

Release files for webhooks-cc 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for webhooks-cc 0.1.0
File Size Uploaded Provenance
webhooks_cc-0.1.0.tar.gz 58.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for webhooks-cc 0.1.0
File Interpreter ABI Platform
webhooks_cc-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 123.2 kB

Release files / webhooks_cc-0.1.0.tar.gz

Download URL webhooks_cc-0.1.0.tar.gz
Size 58.1 kB
Tags Source
SHA-256 checksum
How to use checksums
c949d1e34168ace42ca0124da87e41f7c5aae80d15c8409baff41355301a8256
BLAKE2b-256 checksum
How to use checksums
19720e963f80935138d1a3c35953039e0afff7cade38f988ff71f8fb75b5e022
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Release files / webhooks_cc-0.1.0-py3-none-any.whl

Download URL webhooks_cc-0.1.0-py3-none-any.whl
Size 65.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dae9786a3e69819601a0a5e7a5d17d1189fe735f48a1d49dcbed11f33c9041f4
BLAKE2b-256 checksum
How to use checksums
feecacdb21e74b35f0d7b7b98a0f84a029cc415ee3b60dae7d8fa558a380865f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Security for webhooks-cc 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for webhooks_cc-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page