Debug a failed webhook
Diagnose why a webhook isn't arriving, verify signatures, and replay missed events
Execute curls interactively
To run these API calls directly from your browser, open the Payments API reference or the Auth API reference and use the built-in Try-it playground. Pick Sandbox and send without leaving the docs.
Webhooks can fail for many reasons: a misconfigured URL, a TLS error, a timeout, or a signature mismatch. This recipe walks through the diagnostic steps in order — from the most common issue to the least.
Check the deliveries log
The 505pay dashboard (Settings → Webhooks → Deliveries) shows every delivery attempt with its HTTP status code, response body, and timestamp. Start here — it's faster than any debug session.
Look for:
4xx— your endpoint rejected the request (auth, CORS, body parsing)5xx— your endpoint crashedtimeout— your endpoint took longer than 30 s to respond
Verify your endpoint is reachable
Test-fire a delivery from the API to confirm 505pay can reach your URL:
curl -X POST https://sandbox.505pay.link/v1/payments/settings/webhooks/$WEBHOOK_ID/test \
-H "Authorization: Bearer $ACCESS_TOKEN"If this returns a delivery failure, the issue is network-level (firewall, DNS, TLS cert).
Local development
For local testing, use a tunnel (ngrok, Cloudflare Tunnel, or similar) to expose your localhost to the internet. The deliveries log will show the tunnelled URL.
Verify the signature
If the delivery shows 200 but your handler is discarding the event, the most common cause is a signature mismatch. 505pay signs every webhook with HMAC-SHA256 using your webhook secret.
import crypto from 'node:crypto';
export function verifyWebhookSignature(
rawBody: string,
signature: string, // X-Straventa-Signature header
secret: string,
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}import hmac, hashlib
def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected)function verifySignature(string $rawBody, string $signature, string $secret): bool {
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signature);
}func verifySignature(rawBody []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(rawBody)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expected))
}Common pitfall: computing the HMAC over a parsed body (e.g. JSON.stringify(req.body)) rather than the raw bytes received over the wire. Always read req.rawBody or the equivalent.
Check retry behaviour
505pay retries failed deliveries with exponential backoff: min(2^attempt, 3600) * [0.5–1.5] seconds. If your endpoint was temporarily down, events are retried automatically for up to 72 hours. Check the deliveries log for pending retries.
After 10 consecutive failures, the subscription is automatically disabled and marked dead. Re-enable it from the dashboard or via:
curl -X PUT https://sandbox.505pay.link/v1/payments/settings/webhooks/$WEBHOOK_ID \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"active": true}'Then use the test-fire endpoint (Step 2) to confirm delivery before re-enabling.
Replay a specific event
If you missed events during an outage, fetch the original intent and re-trigger your fulfilment logic:
# Get intent status directly
curl https://sandbox.505pay.link/v1/payments/intents/$INTENT_ID \
-H "Authorization: Bearer $ACCESS_TOKEN"There is no "replay from date" API today — fetch by intent ID if you know which payments were affected.
Checklist
- Deliveries log shows 200 from your endpoint
- HMAC signature computed over raw bytes, not parsed JSON
- Webhook secret matches the one shown in the dashboard
- Endpoint responds within 30 s (defer heavy work to a background job)
- Subscription is active (not
dead)
Related
- Webhooks guide — full signature spec + retry table
- testWebhook API
- updateWebhook API
Was this page helpful?