title: Webhooks
description: Receive real-time event notifications via webhooks.
Webhooks
Webhooks let Wontopos push events to your server in real time — subscriptions, usage alerts, billing, and more.
Create a webhook endpoint
Register a URL to receive webhook events.
POST
/v1/webhooks| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS URL to receive events. |
events | string[] | Yes | List of event types to subscribe to. |
description | string | No | Human-readable label for this endpoint. |
secret | string | No | Custom signing secret. Auto-generated if omitted. |
curl https://api.wontopos.com/v1/webhooks
-X POST
-H "Authorization: Bearer sk_live_your_key_here"
-H "Content-Type: application/json"
-d '{
"url": "https://yourapp.com/webhooks",
"events": ["subscription.created", "usage.limit_reached"],
"description": "Production webhook"
}' Response:
{
"id": "wh_abc123",
"url": "https://yourapp.com/webhooks",
"events": ["subscription.created", "usage.limit_reached"],
"secret": "whsec_...",
"status": "active",
"created_at": "2026-03-30T10:00:00Z"
} Save the secret
The webhook secret is only returned at creation time. Store it securely for signature verification.
Event types
| Event | Description |
|---|---|
subscription.created | A new subscription was created |
subscription.updated | A subscription was modified (plan change, etc.) |
subscription.cancelled | A subscription was cancelled |
usage.limit_reached | Usage has reached 80% of plan limit |
usage.limit_exceeded | Usage has exceeded plan limit |
invoice.created | A new invoice was generated |
invoice.paid | An invoice was successfully paid |
invoice.failed | A payment attempt failed |
api.updated | A subscribed API published a new version |
Payload structure
All webhook payloads follow the same envelope format:
{
"id": "evt_abc123",
"type": "subscription.created",
"api_version": "v1",
"created": 1711785600,
"data": {
"object": {
"id": "sub_9x8y7z",
"api_id": "api_1a2b3c4d",
"plan_id": "plan_pro",
"status": "active"
}
}
} | Field | Type | Description |
|---|---|---|
id | string | Unique event ID (use for idempotency) |
type | string | Event type |
api_version | string | API version that generated the event |
created | integer | Unix timestamp |
data.object | object | The resource that triggered the event |
Signature verification
Every webhook request includes a Wontopos-Signature header. Always verify it to prevent spoofed events.
The signature is an HMAC-SHA256 hex digest of the raw request body, using your webhook secret as the key.
// Node.js
const crypto = require("crypto");
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
} # Python
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature) // Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func verifyWebhook(payload []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
} Always verify signatures
Never process webhook events without verifying the signature. Failing to do so makes your application vulnerable to spoofing and replay attacks.
Retry policy
If your endpoint returns a non-2xx status code or does not respond within 30 seconds, Wontopos retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 24 hours |
After 5 failed retries, the event is marked as failed. You can manually retry failed events from the dashboard.
Idempotency
Use the `id` field in the event payload to deduplicate. The same event may be delivered more than once.