Receive real-time HTTP POST requests when payment events occur.
Growth+ Plan Feature. Webhooks are available on Growth and Scale plans.
Go to Dashboard → Settings
Provide an HTTPS endpoint that SoftChurn should POST events to. Example: https://yourapp.com/webhooks/softchurn
SoftChurn will generate a signing secret. Copy this and store it securely—you'll use it to verify webhook authenticity.
Use Settings → "Send test webhook" to verify your endpoint receives events correctly.
Sent when a payment attempt fails (card decline, insufficient funds, etc.)
Sent when a failed payment is successfully recovered through retry.
Sent when a customer cancels their subscription after payment recovery fails.
Each webhook request is a signed HTTP POST with the following headers and body:
POST /webhooks/softchurn HTTP/1.1
Host: yourapp.com
Content-Type: application/json
X-SoftChurn-Signature: sha256=abc123def456...
X-SoftChurn-Event: payment.failed
X-SoftChurn-Timestamp: 1712614400
{
"event": "payment.failed",
"timestamp": 1712614400,
"data": {
"invoice_id": "in_1234567890",
"charge_id": "ch_1234567890",
"amount": 29.99,
"currency": "usd",
"customer_email": "[email protected]",
"customer_name": "John Doe",
"failure_code": "card_declined",
"decline_type": "hard"
}
}
Never skip signature verification. An unverified endpoint can be spoofed — a malicious request could trigger state changes in your app.
Always verify the X-SoftChurn-Signature header to ensure the webhook came from SoftChurn:
$secret = 'your_signing_secret';
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SOFTCHURN_SIGNATURE'] ?? '';
$expected = hash_hmac(
'sha256',
$payload,
$secret
);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
die('Invalid signature');
}
$data = json_decode($payload, true);
// Process event...
const crypto = require('crypto');
const secret = 'your_signing_secret';
const payload = req.rawBody;
const signature = req.headers[
'x-softchurn-signature'
];
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (expected !== signature) {
res.status(401).send('Invalid signature');
return;
}
const data = JSON.parse(payload);
// Process event...
SoftChurn automatically retries failed webhook deliveries with exponential backoff:
Check the delivery status in your API (Scale plan): GET /api/v1/webhooks/deliveries
Tip: Queue webhook processing in a background job and return 200 immediately. Endpoints slower than 10s will be treated as failed.