Webhooks#
Woldy uses webhooks to notify your server about events — for example, when a payment is successfully completed or has expired. You need to create an HTTP endpoint that Woldy will send POST requests to.
Quick Start#
1.
Create an HTTPS endpoint to receive webhook requests
2.
Register a webhook in the dashboard under Settings → Webhooks 3.
Save the Public Key shown in the dashboard — you will need it to verify signatures
4.
Verify the signature of every incoming request
5.
Return HTTP 2xx in response to each request
Setup#
Webhook creation and management is available through the dashboard under Settings → Webhooks.When creating a webhook, specify:URL — your server's HTTPS endpoint (HTTP is not supported)
Events — the event types to subscribe to
After creation, you will be shown the Public Key (Ed25519, base64). Save it — it is displayed only once.Limits: max 10 webhooks per branch, HTTPS only, private IPs and localhost are not allowed.
Incoming Request#
Each webhook is a POST request with Content-Type: application/json.| Header | Value |
|---|
X-Woldy-Signature | Ed25519 signature, base64 Standard Encoding (RFC 4648, with = padding) |
X-Woldy-Signature-Version | Always ed25519-v1 |
X-Woldy-Event-ID | Event UUID — xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
X-Woldy-Event-Type | Event type, e.g. payment.created |
X-Woldy-Timestamp | Unix timestamp of the request (integer, seconds) |
Content-Type | application/json |
User-Agent | Woldy-Webhooks/1.0 |
Body example#
{
"id": "wev_VQ6EA4np...",
"branch_id": "cb_VQ6EA4np...",
"webhook_id": "wh_VQ6EA4np...",
"type": "payment.completed",
"api_version": "2026-01-01",
"created_at": 1737720000,
"data": {
"id": "p_VQ6EA4np...",
"amount": "100.50",
"status": "COMPLETED",
"currency": "USDT"
}
}
Signature Verification#
Always verify the signature before processing event data.
How it works#
Woldy signs every request using Ed25519. The signed string is:{timestamp}.{eventID}.{bodyJSON}
timestamp — value from X-Woldy-Timestamp
eventID — value from X-Woldy-Event-ID (raw UUID)
bodyJSON — the exact raw bytes of the HTTP request body
The signature covers the entire request body. Always verify against the raw body bytes — never re-serialize parsed JSON.
Steps#
1.
Extract X-Woldy-Timestamp, X-Woldy-Event-ID, X-Woldy-Signature from headers
2.
Check freshness: |now - timestamp| ≤ 300 seconds
3.
Build: "{timestamp}.{eventID}.{bodyJSON}"
4.
Decode X-Woldy-Signature from base64 Standard Encoding
5.
Verify Ed25519 signature with the Public Key from the dashboard
Node.js
Python
Go
Retry Policy#
If your server returns an error (4xx, 5xx) or does not respond within 10 seconds:| Attempt | Delay |
|---|
| 1 | Immediately |
| 2 | ~1 minute |
| 3 | ~5 minutes |
| 4 | ~30 minutes |
| 5 | ~2 hours |
After 5 failed attempts the delivery is marked FAILED.
Best Practices#
Respond quickly — return 200 immediately, process asynchronously.Handle duplicates — the same event may arrive more than once. Use uid for deduplication.Always verify — never process a webhook without verifying the signature.
Troubleshooting#
Not receiving events — check webhook status in the dashboard (ACTIVE?), confirm you subscribed to the correct event type.Use the Public Key from the dashboard (shown once at creation)
Sign only the data field, not the full body
Do not re-sort keys — serialize data exactly as received (no whitespace, key order from the body)
X-Woldy-Event-ID is a raw UUID, not wev_...
Timestamp must be within ±5 minutes
Decode signature from base64 Standard Encoding (with = padding)
Public Key is raw 32-byte Ed25519, not X.509/SPKI
Timeout — respond 200 immediately, process asynchronously.Modified at 2026-03-18 07:51:55