Use case

Webhooks for auth and lifecycle events

AscendKit emits configurable webhooks for auth and lifecycle events with event filtering, signature verification, and test delivery. Both the JS and Python SDKs verify signatures, so your backend can react to sign-ups, journey transitions, and survey responses securely.

How it works

  1. 1. Configure an endpoint
    Register a webhook endpoint in the portal and filter it to the events you care about.
  2. 2. Verify signatures
    Use the SDK signature verification in JS or Python to confirm each delivery is authentic.
  3. 3. Test delivery
    Send a test event from the portal to confirm your endpoint handles the payload before going live.

What should you use webhooks for here?

Reacting in your own systems — not synchronising user records, which is the job AscendKit exists to remove. If you find yourself writing a webhook handler that copies users into another database, step back and ask why that second copy exists.

The good uses are outward: post to Slack when someone signs up, open a ticket when a detractor NPS response arrives, kick off provisioning in your own infrastructure, write an audit entry into your compliance store.

The distinction matters because sync webhooks are the fragile kind. They must be reliable or your data drifts. Reaction webhooks can fail and be retried without anything becoming inconsistent, because they are not the source of truth for anything.

Two kinds of webhook consumer.
PurposeFailure impactRecommended
Copy users into another systemSilent data driftAvoid -- this is the problem, not the solution
Notify a channel or personA missed messageYes
Trigger provisioningRetryableYes
Write an audit recordRetryableYes

How do you verify a webhook safely?

Check the HMAC-SHA256 signature against the exact bytes we sent, before parsing anything. An unverified webhook endpoint is an unauthenticated write path into your systems, and it will be found.

The single most common implementation bug is re-serialising the payload before verification. Parsing JSON and re-encoding it produces a different byte sequence and the signature will never match, no matter how correct the secret is.

Timestamp tolerance is the other half. Verification enforces a five-minute window to prevent replay, so a captured request cannot be resent later — which also means server clock drift shows up as signature failures.

webhook_handler.py
from fastapi import Request, Response
from ascendkit import verify_webhook_signature
import os

SECRET = os.environ["ASCENDKIT_WEBHOOK_SECRET"]

@app.post("/webhooks/ascendkit")
async def handle(request: Request) -> Response:
    body = await request.body()          # raw bytes -- never request.json()
    if not verify_webhook_signature(
        secret=SECRET,
        signature_header=request.headers.get("x-ascendkit-signature", ""),
        payload=body.decode(),
    ):
        return Response(status_code=401, content="Invalid signature")

    event = await request.json()         # safe to parse only after verifying
    return Response(status_code=200, content="OK")

Troubleshooting

Signature verification always fails

Cause: The payload was parsed and re-serialised before verification, so the bytes no longer match what was signed.

Fix: Verify against the raw body -- await request.body() in FastAPI, request.get_data(as_text=True) in Flask. If the raw body is correct and it still fails, check server clock accuracy against the five-minute replay window.

The same event arrives more than once

Cause: Expected. Delivery is at-least-once, so a slow or failed acknowledgement causes a retry.

Fix: Make handlers idempotent by keying on the event ID. Never assume exactly-once delivery from any webhook provider.

FAQ

Are webhook payloads signed?

Yes. Deliveries are signed and both SDKs include signature verification helpers so you can reject forged requests.

Can I filter which events I receive?

Yes. Endpoints support event filtering, so you only receive the auth and lifecycle events relevant to your integration.

Related guides

Start with one API key

Auth, email, surveys, and journeys share one user record, so you ship this without stitching vendors together.

Start free