Webhook Notifications

Receive real-time HTTP POST requests when payment events occur.

Growth+ Plan Feature. Webhooks are available on Growth and Scale plans.

Setup

  1. 1.

    Navigate to Settings

    Go to Dashboard → Settings

  2. 2.

    Enter your webhook URL

    Provide an HTTPS endpoint that SoftChurn should POST events to. Example: https://yourapp.com/webhooks/softchurn

  3. 3.

    Copy your signing secret

    SoftChurn will generate a signing secret. Copy this and store it securely—you'll use it to verify webhook authenticity.

  4. 4.

    Test with our CLI

    Use Settings → "Send test webhook" to verify your endpoint receives events correctly.

Event Types

payment.failed

Sent when a payment attempt fails (card decline, insufficient funds, etc.)

payment.recovered

Sent when a failed payment is successfully recovered through retry.

subscription.canceled

Sent when a customer cancels their subscription after payment recovery fails.

Request Format

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"
  }
}

Signature Verification

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:

PHP

$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...

Node.js

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...

Retry Logic

SoftChurn automatically retries failed webhook deliveries with exponential backoff:

  • Attempt 1: 5 minutes after initial failure
  • Attempt 2: 30 minutes after previous attempt
  • Attempt 3: 2 hours after previous attempt
  • Attempt 4: 8 hours after previous attempt
  • After 4 failed attempts, the webhook is marked as abandoned

Check the delivery status in your API (Scale plan): GET /api/v1/webhooks/deliveries

Best Practices

Tip: Queue webhook processing in a background job and return 200 immediately. Endpoints slower than 10s will be treated as failed.

  • Respond quickly: Return a 200 status code immediately. Process the event asynchronously in a background job.
  • Verify signatures: Always verify the X-SoftChurn-Signature header before processing.
  • Handle duplicates: Process the same event ID idempotently—duplicates may be retried.
  • Monitor deliveries: Use the API to check webhook status and investigate failures.
  • Use HTTPS: Always provide an HTTPS endpoint for security.