Configure webhook endpoints to receive real-time events from AscendKit.

Webhooks

Receive real-time notifications on your server when supported events happen in AscendKit. Webhooks let your backend react immediately to user signups, logins, survey submissions, and email lifecycle updates without polling the API.

Use cases: sync new users to your CRM, send a Slack notification on login, update internal records when a survey is submitted, or mirror email delivery events into your own logs.

Every webhook request is signed with HMAC-SHA256 so your server can verify it came from AscendKit. See Python SDK — Webhook verification or the Next.js Integration for implementation examples.

Create a webhook

ascendkit webhook create \
  --url https://yourapp.com/webhooks/ascendkit \
  --events user.created,user.login
FlagDescription
--urlYour endpoint URL (must be HTTPS in production)
--eventsComma-separated list of event types to subscribe to

List webhooks

ascendkit webhook list

View a webhook

ascendkit webhook get whc_abc123

Update a webhook

ascendkit webhook update whc_abc123 \
  --url https://yourapp.com/webhooks/v2 \
  --events user.created,user.login

Test a webhook

Send a test event to verify your endpoint is working:

ascendkit webhook test whc_abc123

Delete a webhook

ascendkit webhook delete whc_abc123

Payload shape

Webhook requests are JSON. The canonical event field is event. eventType is also included as a compatibility alias.

{
  "event": "user.created",
  "eventType": "user.created",
  "timestamp": "2026-05-04T12:00:00Z",
  "projectId": "prj_abc123",
  "environmentId": "env_abc123",
  "data": {
    "userId": "usr_abc123",
    "email": "[email protected]"
  }
}

The same event type is also sent in the X-AscendKit-Event header. Verify the HMAC signature before trusting the payload.

Available events

EventDescription
user.createdUser signs up (any method)
user.loginUser logs in
user.logoutUser logs out
survey.completedUser submits a survey response
email.sentEmail queued or sent
email.deliveredEmail delivery confirmed
email.bouncedEmail bounced
email.complainedComplaint received
email.openedTracked email open
email.clickedTracked email link click
email.unsubscribedUser unsubscribed from campaign email

Verifying a delivery

Every delivery carries an HMAC-SHA256 signature in the x-ascendkit-signature header. Verify it before parsing the body. An unverified endpoint is an unauthenticated write path into your systems.

Verify against the raw bytes we sent. The most common implementation bug is parsing JSON and re-encoding it before verification — that produces a different byte sequence and the signature will never match, regardless of how correct your secret is.

body = await request.body()          # raw -- not request.json()
verify_webhook_signature(
    secret=SECRET,
    signature_header=request.headers.get("x-ascendkit-signature", ""),
    payload=body.decode(),
)

Verification also enforces a five-minute timestamp tolerance to prevent replay, so significant server clock drift shows up as signature failures.

Delivery semantics

Delivery is at-least-once. A slow or failed acknowledgement causes a retry, so the same event can arrive more than once.

Make handlers idempotent by keying on the event ID. Never assume exactly-once delivery — no webhook provider offers it, and designing as though yours does produces duplicate side effects under exactly the conditions retries exist for.

Return 2xx quickly. If your handler does slow work before responding, deliveries time out and retry, and you get the duplicate you were trying to avoid. Acknowledge first, then queue the work.

What not to use webhooks for

Copying user records into another system. That second copy is the problem AscendKit exists to remove — auth, email, journeys, and surveys already read the same usr_ profile, so there is nothing to synchronise.

The useful consumers are outward-facing:

PurposeFailure impactRecommended
Copy users into another systemSilent data driftAvoid
Notify a channel or personA missed messageYes
Trigger provisioningRetryableYes
Write an audit recordRetryableYes

Sync webhooks must be reliable or your data diverges. Reaction webhooks can fail and retry without anything becoming inconsistent, because they are not the source of truth for anything.

Troubleshooting

Signature verification always fails

The payload was re-serialised before verification. Use the raw body. If that is already correct, check server clock accuracy against the five-minute replay window.

Deliveries stop after a period of failures

Endpoints that fail repeatedly are backed off to protect both sides. Fix the endpoint, then use webhook test to confirm it responds before expecting normal delivery to resume.

Events fire in development but not production

Webhooks are configured per environment. Check which environment is active with ascendkit environment show — see CLI Setup.

Related

  • Python SDKverify_webhook_signature and the rest of the surface
  • Journeys — react to lifecycle events without leaving the platform
  • Surveys — survey submissions are a common webhook trigger

Choosing which events to subscribe to

Subscribe narrowly. An endpoint receiving every event has to filter in your code, and every event you do not act on is still a request your server must authenticate, parse, and acknowledge.

Event filtering is configured per endpoint, so a Slack notifier and a provisioning service can subscribe to different sets rather than sharing one firehose. Separate endpoints also fail independently — a broken provisioning handler does not back off your notification deliveries.