Skip to main content

Check the basics first

Every delivery carries X-Pazy-Webhook-Attempt and X-Pazy-Webhook-Subscription-Id. Log them, along with the event id and your response status. Most problems show up straight away in those logs.
Work through these in order:
  1. Raw body. Verify against the exact bytes received. If your framework parses JSON first, configure it to keep the raw body (express.raw in Express, request.get_data() in Flask). Never re-serialize the parsed object.
  2. Secret. Use the secret for this endpoint. Sandbox and production endpoints have different secrets. Check there’s no trailing whitespace or newline from an environment file.
  3. Signed string. It is <timestamp>.<raw_body>, with a literal dot, where the timestamp is the header value as sent.
  4. Prefix. The header looks like sha256=<hex>. Strip the prefix before comparing, or compare against the prefixed value.
  5. Known-good test. Run your code against the test vector. If it fails there, the bug is in your HMAC code and not in your setup.
Your timestamp check rejects requests more than 5 minutes from your server’s clock. Check that your server’s clock is synced with NTP. Retries are re-signed with a fresh timestamp each time, so a late retry isn’t rejected for its age.
  • Confirm Pazy has your endpoint registered for the right environment (sandbox or production) and organization.
  • Confirm you’re subscribed to the event type. An event you aren’t subscribed to is never sent.
  • Confirm the action that raises the event actually happened. For example, payment.invoice.completed fires when money reaches a final outcome, not when a payment request is created.
  • Check the URL requirements. In production, http://, localhost, private IPs and redirecting URLs all fail.
  • Check that your firewall allows Pazy’s egress IPs. Ask support for the list.
  • Check your server and load balancer logs. Is the request reaching your app?
If all of that looks right, contact customer-support@pazy.io with the endpoint URL and the approximate time of the event.
That’s expected. Delivery is at-least-once, so duplicates happen. Deduplicate on the event id, as described in Delivery and retries.If duplicates are frequent, your endpoint is probably responding too slowly or with a non-2xx status, so Pazy retries. Check the X-Pazy-Webhook-Attempt header: values above 1 confirm retries. Acknowledge faster and move processing to a background job.
Pazy waits 10 seconds by default, and 30 at most. Your handler should return in well under a second:
  • Don’t call other services, and don’t run heavy database work, before responding.
  • Save the event or enqueue it, then return 200.
  • If you need more time for cold starts, ask support to raise your timeout.
  • Redirects count as failures. If your URL redirects (for example http to https, or a trailing-slash redirect), register the final URL.
  • A response body over 64 KB counts as a failure. Return an empty body.
  • Check that the 200 really comes from your handler and not from a proxy or WAF that then drops the connection.
Ordering isn’t guaranteed. A retried event can arrive after a newer one. Compare timestamps in the payload, or fetch the current state from the API. See Don’t rely on ordering.
After 5 attempts (about 8 minutes) Pazy stops retrying. Reconcile with the API by listing recent records, and contact customer-support@pazy.io if you need events replayed. Self-serve redelivery is coming soon.
Check the event’s page in the event reference. Fields marked nullable are present with a null value.

Still stuck?

Email customer-support@pazy.io with:
  • The endpoint URL and environment
  • The event id and type (or the approximate time)
  • The X-Pazy-Webhook-Attempt value and the status your endpoint returned