Webhooks
Receive real-time notifications when events occur in your PayForge account.
How Webhooks Work
When an event occurs (e.g., a payment is captured), PayForge sends an HTTP POST request to your configured endpoint with the event data. Your endpoint should return a 2xx status to acknowledge receipt.
Retry Policy
5 attempts over 24h
Timeout
30 seconds
Signature
HMAC-SHA256
Webhook Payload
Example webhook payloadjson
{
"id": "evt_01ABC...",
"type": "payment.captured",
"created_at": "2026-03-06T10:31:00Z",
"data": {
"id": "pay_01HXYZ...",
"amount_cents": 5000,
"currency": "USD",
"status": "captured",
"merchant_id": "mer_01ABC...",
"fraud_score": 0.03
}
}Event Types
| Event | Description |
|---|---|
| payment.initiated | Payment created and queued for processing |
| payment.authorized | Network authorization successful |
| payment.authorization_failed | Network declined the authorization |
| payment.fraud_passed | Fraud check passed, payment cleared |
| payment.fraud_blocked | Fraud check blocked the payment |
| payment.captured | Payment captured and funds secured |
| payment.settled | Settlement complete, funds transferred |
| payment.refunded | Full refund processed |
| payment.partially_refunded | Partial refund processed |
| payment.voided | Authorization voided before capture |
| payment.dispute_opened | Dispute or chargeback opened |
| payment.dispute_resolved | Dispute resolved |
Signature Verification
Every webhook includes a X-PayForge-Signature header. Always verify signatures to ensure the webhook came from PayForge. The signature format is t=timestamp,v1=signature.
Verify webhook signature
def verify_webhook(payload, signature_header, secret)
parts = signature_header.split(",").map { |p| p.split("=", 2) }.to_h
timestamp = parts["t"]
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.#{payload}")
# Timing-safe comparison prevents timing attacks
Rack::Utils.secure_compare(expected, parts["v1"])
end
# Usage in a Rails controller
class WebhooksController < ApplicationController
def receive
payload = request.body.read
sig = request.headers["X-PayForge-Signature"]
unless verify_webhook(payload, sig, ENV["PAYFORGE_WEBHOOK_SECRET"])
head :unauthorized
return
end
event = JSON.parse(payload)
# Handle the event...
head :ok
end
endRetry Schedule
Failed deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry (final) | 24 hours |
Testing Webhooks
Use the test endpoint to send a sample webhook event to your configured URL:
Send test webhookbash
curl -X POST "https://api.payforge.dev/api/v1/webhooks/test" \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "event_type": "payment.captured" }'You can also view delivery history in the dashboard under Webhooks → Deliveries to debug failed deliveries and inspect payloads.