VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Essentials
API overview
Authentication
Event capture
Push device API
Webhooks
Webhook delivery
Webhook events
Projects & keys
Projects
API keys
Audit log
Customers & access
Persons
Products
Perks
Sandbox
Paywalls
Paywalls
Paywall locations
Components
Component registry
Assets
Experiments & flags
Experiments
Feature flags
Analytics & events
Analytics
Analytics alerts
Events
Ingest policy
VoidQL
Tracking domains
Ads
Ads
Ad accounts
Meta measurement
Payments & stores
Payment providers
Payment provider products
Purchase history imports
Payment sources
Webhooks & notifications
Webhooks
Push notifications
Push configurations
Automations
Organization & team
Organizations
Members
Invitations
Billing
Your account
Profile
Session
Notices
Connected apps
Terms of Service
Developer tools
Schema
SDK

Webhook delivery

How Voidhash signs, sends, retries and logs webhook requests.

A webhook is an HTTPS endpoint on your server that Voidhash calls when something changes in your project: a subscription renews, a purchase is refunded, a person's data is erased. Renewals, cancellations and refunds happen at the store, often while the app is closed, so webhooks are how your backend hears about them.

This page describes what Voidhash sends and when. To build the receiving endpoint, follow the guide for Node.js, PHP, Go, Rust or another language. Webhook events lists every event type and payload.

A webhook tells you that something changed, not what the person can use now. For a yes/no access decision, check access from your backend instead.

Endpoints

In Studio, open Settings → Webhooks and select New webhook. Fill in:

The new webhook endpoint form: name, URL, events, description and environment

  • Name: what the endpoint is for, such as Orders backend. Up to 100 characters.
  • URL: the public URL Voidhash sends POST requests to.
  • Environment: Production or Test. See Environments.
  • Events: the event types this endpoint receives. Select at least one.
  • Description (optional): a note for your team.

On Voidhash Cloud the URL must resolve to a public address. localhost and private network addresses are refused, both when you save and on every delivery.

Select Create webhook. Studio shows the endpoint's signing secret once: whsec_ followed by 64 hex characters. Store it in your server's secret manager next to your secret key.

To send a signed test.ping event, open the endpoint's actions menu and select Send test. The delivery appears under Recent deliveries with your server's answer. A test only goes to an endpoint that is Active.

You can also manage endpoints with the CLI (voidhash webhook create, edit, enable, disable, rotate, test, deliveries, retry), the API, or a server library.

Environments

Every endpoint belongs to production or test, like API keys. The environment decides which purchase events the endpoint receives:

EndpointReceives purchase events from
productionProduction purchases, plus store sandbox purchases while they unlock production apps.
testStore sandbox purchases (TestFlight, Google Play testing), simulated purchases from development mode, and test mode payments such as Stripe test mode.

person.deleted belongs to no purchase, so it reaches every endpoint that subscribes to it. Point a test endpoint at your staging server.

What Voidhash sends

Each event is one POST request with a JSON body and three headers:

HeaderValue
X-Webhook-EventThe event type, such as subscription.renewed.
X-Webhook-TimestampThe Unix time, in seconds, when this attempt was signed.
X-Webhook-Signaturev1= and an HMAC-SHA256 signature. See Signatures.

The body is the event payload itself, with no envelope around it. Its type field repeats the event type:

purchase.completed
{
  "type": "purchase.completed",
  "projectId": "proj_r3k8v2m1x9q4",
  "personId": "person_k2v9x4m8q1r3",
  "distinctId": "user_123",
  "productId": "prod_x7m2k9v4q8r1",
  "productSlug": "lifetime",
  "providerProductId": "com.example.lifetime",
  "provider": "apple-app-store",
  "source": null,
  "verified": true,
  "environment": "production",
  "occurredAt": "2026-11-05T09:12:44.000Z",
  "purchaseId": "pur_v8q2m4k9x1r7",
  "purchaseKind": "one_time",
  "providerKey": "2000000123456789",
  "providerTransactionId": "2000000123456789",
  "purchasedAt": "2026-11-05T09:12:44.000Z",
  "amount": { "currency": "USD", "grossAmount": 4999 }
}

Webhook events lists every field and every event type.

Signatures

Voidhash signs every request with the endpoint's signing secret. Anyone who knows your URL can send it a forged event, so verify the signature before you trust a request.

  • X-Webhook-Signature is v1= followed by a lowercase hex HMAC-SHA256 digest.
  • The digest covers the string {timestamp}.{body}: the X-Webhook-Timestamp value, a period, and the raw request body exactly as received.
  • The HMAC key is the full secret string, whsec_ prefix included, encoded as UTF-8.
  • v1 names the scheme. Reject signatures with any other prefix.

The server libraries reject a timestamp more than 300 seconds from your server's clock, in either direction, so a captured request can't be replayed later. Every retry is signed again with a new timestamp, so a retried delivery passes this check. To verify without a library, see Receive webhooks over HTTP.

Respond to deliveries

Answer with any 2xx status within 30 seconds. Anything else counts as a failed attempt: a 3xx (redirects are not followed), a 4xx or 5xx, a timeout, or a connection error.

Verify the signature, store the event, answer 200, and do the work afterwards. A handler that does slow work before it answers times out and receives the same event again.

Retries

A failed delivery is retried on a fixed schedule, measured from the previous attempt:

AttemptWhen
1Right after the event
25 minutes after attempt 1
330 minutes later
42 hours later
524 hours later

After the fifth failed attempt the delivery is exhausted and is not retried again. Each retry goes to the endpoint's current URL and is signed with its current secret, with a new timestamp.

Automatic disabling

After 20 consecutive failed attempts across all of an endpoint's deliveries, Voidhash sets the endpoint to Failed and stops sending to it. Any successful delivery resets the count.

A Failed or Disabled endpoint receives no new events, and events that happen meanwhile are not queued for it. To turn it back on, select Edit in its actions menu and switch Enabled on. That also clears the failure count.

Delivery log

Select an endpoint in Settings → Webhooks to see its Recent deliveries. Each delivery has a status:

A webhook endpoint in Studio, with its events, environment and recent deliveries

StatusMeaning
pendingWaiting for its first attempt.
in_progressBeing sent.
succeededYour endpoint answered 2xx.
failedThe last attempt failed; Next retry shows when the next one runs.
exhaustedAll 5 attempts failed.

Open a delivery to see its Payload and its Delivery attempts: each attempt's status code, duration, error and up to 2 KB of your response body.

To send a failed or exhausted delivery again now, open it and select Retry. The retry reuses the original payload.

Duplicates and ordering

Delivery is at least once. A timeout after your server processed an event, or a manual retry, sends the same event again. Payloads carry no event ID, so build an idempotency key from type, the subject's ID (subscriptionId, purchaseId or personId) and occurredAt. Record each key you process and skip ones you have seen.

Events are not guaranteed to arrive in order, because each delivery retries on its own schedule. Compare occurredAt before you overwrite newer state, or check access for the current answer.

Rotate the secret

Rotate a secret that may have leaked: select Rotate secret in the endpoint's actions menu, or run voidhash webhook rotate. Studio shows the new secret once.

The old secret stops working at once

Every delivery after rotation, including retries of earlier events, is signed with the new secret. Deploy the new secret right away; deliveries your server rejects in the meantime fail and are retried on the schedule above.

Next steps

  • Webhook events: every event type and its payload.
  • Receive webhooks with Node.js, PHP, Go, Rust or another language.
  • Webhook endpoints in the API

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousPush device APINextWebhook events

On this page

EndpointsEnvironmentsWhat Voidhash sendsSignaturesRespond to deliveriesRetriesAutomatic disablingDelivery logDuplicates and orderingRotate the secretNext steps