Skip to main content
A webhook is an HTTP POST from Creptapay to a URL you control, sent when something happens in your account. They are how you find out a payment confirmed on-chain without polling for it, and the only reliable way to know a subscription has lapsed. Register endpoints in the dashboard under Developer → Webhooks, or over the API — see Managing endpoints below.

The envelope

Every delivery has the same shape. Only data changes between events.

Headers

Verify the signature on every request before you act on it — see Verify Signature. An unverified webhook endpoint is a URL that anyone who guesses it can use to mark orders as paid.

Delivery

Be aware of how delivery actually behaves before you build on it:
We attempt each delivery once. If your endpoint is down, times out, or returns a non-2xx status, the event is recorded as failed and is not retried automatically. Resend it from Developer → Logs in the dashboard.
  • Respond within 10 seconds. We give up after that and mark the delivery failed, even if your handler eventually finishes. Acknowledge with a 200 first and do the slow work afterwards.
  • Return 2xx to acknowledge. Any other status counts as a failure.
  • Order isn’t guaranteed. Deliveries go out in parallel, so payment.paid can reach you before payment.created. Treat each event as a statement about the object’s current state rather than a step in a sequence.
  • Handle duplicates. A resent delivery repeats an event you may already have processed. Key your handler on the object’s id and the event name, and make repeat deliveries a no-op.
  • Reconcile, don’t rely. Because there are no automatic retries, a webhook outage on your side loses events. For anything that must not be missed — granting access, releasing goods — reconcile against the REST API on a schedule as well.

Environments

An endpoint is scoped to sandbox, to production, or to both. One registered for a single environment only ever receives that environment’s events, so you can point sandbox at a tunnel on your laptop and production at your real server without them crossing. Each environment signs with its own secret key. A live delivery is signed with your live key, and you cannot verify it with your test key.

Event catalogue

Payments

data is the payment object:
transactionHash is added on payment.paid and payment.underpaid; it is the same value as tx_hash, kept for older integrations. status is overpaid rather than paid when more arrived than was owed. Both mean the payment settled — check for either, or use payment.paid as the trigger and read amount_paid for the figure.

Subscriptions

The two that matter are subscription.activated and subscription.expired. Wire those and you have a working integration; the rest is refinement. data is the subscription object, with plan and customer always expanded:
Read the price from terms, not from plan. terms is what this subscriber actually pays, frozen when they signed up; plan may since have been repriced for new customers. When the two differ, on_legacy_terms is true. See Subscriptions.
Subscription payments fire the payment events too. A renewal arrives as a payment.paid with type: "subscription", the subscription’s id in subscription, and the cycle it covers in billing_period_start and billing_period_end — so an existing payments integration keeps working unchanged. If you handle both, make sure you don’t fulfil twice.

Managing endpoints

In the dashboard: Developer → Webhooks → Add endpoint, then pick the events you want. Developer → Logs shows every delivery with the response we got back, and lets you resend one.

Over the API

Endpoints can also be managed with your secret key, which is how an installable integration registers itself. The key decides the environment — a test key registers a sandbox endpoint, a live key a production one.
Registering is an upsert keyed on the URL. Posting the same URL again updates its event list instead of creating a second endpoint, so an integration can call this on every startup without piling up duplicates.
An unknown event name is rejected with Unsupported events: ... rather than silently ignored, so a typo fails loudly instead of leaving you waiting for a delivery that never comes. List your endpoints for the key’s environment:
Remove one by URL:
Send a test delivery to check an endpoint is reachable and your signature check passes, before you depend on it:
This POSTs a signed webhook.test event — same envelope, same headers, same signature — and reports back what your server returned:
The URL doesn’t have to be registered first. Handle webhook.test by verifying the signature and returning 200; there’s nothing to act on.

Securing your endpoint

Verify the signature on every request, and only then act on the payload. Step by step, with code for Node, Python, PHP and Java: Verify Signature.