Skip to main content

Webhooks

Config.money sends webhook events to notify you of important payment lifecycle events. Configure your webhook endpoint in your merchant dashboard to receive these events.

Endpoint

POST https://your-domain.com/webhook

Headers

Each webhook request includes the following headers:
Content-Type: application/json
X-Config-Signature: t=1679580000,v1=5257a869e7eaea848cde13e1358682c04f21cf0c
The X-Config-Signature header contains a timestamp and signature that you can use to verify the webhook’s authenticity.

Event Types

Schedule Events

schedule.setup_success

Sent when a customer successfully sets up a collection schedule.
{
  "event": "schedule.setup_success",
  "data": {
    "schedule_id": "sched_123456789",
    "customer_id": "cust_123456789",
    "amount": {
      "value": 2000,
      "currency": "GBP"
    },
    "frequency": {
      "type": "monthly",
      "day": 1
    },
    "created_at": "2024-03-20T10:00:00Z"
  }
}

schedule.setup_failed

Sent when a customer fails to set up a collection schedule.
{
  "event": "schedule.setup_failed",
  "data": {
    "schedule_id": "sched_123456789",
    "customer_id": "cust_123456789",
    "error": {
      "code": "consent_denied",
      "message": "Customer denied consent for the schedule"
    },
    "created_at": "2024-03-20T10:00:00Z"
  }
}

Collection Events

collection.success

Sent when a collection is successfully made from the customer’s account to your merchant wallet.
{
  "event": "collection.success",
  "data": {
    "payment_id": "pay_123456789",
    "schedule_id": "sched_123456789",
    "amount": {
      "value": 2000,
      "currency": "GBP"
    },
    "completed_at": "2024-03-20T10:00:00Z"
  }
}

collection.failed

Sent when a collection fails.
{
  "event": "collection.failed",
  "data": {
    "payment_id": "pay_123456789",
    "schedule_id": "sched_123456789",
    "amount": {
      "value": 2000,
      "currency": "GBP"
    },
    "error": {
      "code": "insufficient_funds",
      "message": "Insufficient funds in customer account"
    },
    "failed_at": "2024-03-20T10:00:00Z"
  }
}

Settlement Events

settlement.success

Sent when funds are successfully settled from your merchant wallet to your destination account.
{
  "event": "settlement.success",
  "data": {
    "settlement_id": "set_123456789",
    "amount": {
      "value": 2000,
      "currency": "GBP"
    },
    "destination_account": {
      "account_number": "12345678",
      "sort_code": "123456"
    },
    "completed_at": "2024-03-21T10:00:00Z"
  }
}

settlement.failed

Sent when a settlement fails.
{
  "event": "settlement.failed",
  "data": {
    "settlement_id": "set_123456789",
    "amount": {
      "value": 2000,
      "currency": "GBP"
    },
    "destination_account": {
      "account_number": "12345678",
      "sort_code": "123456"
    },
    "error": {
      "code": "invalid_account",
      "message": "Invalid destination account details"
    },
    "failed_at": "2024-03-21T10:00:00Z"
  }
}

Response

Your webhook endpoint should return a 200 OK response to acknowledge receipt of the webhook. If we don’t receive a 200 OK response, we’ll retry the webhook delivery.

Retry Policy

If your webhook endpoint is unavailable or returns an error, we’ll retry the delivery with the following schedule:
  1. 1 minute after the first failure
  2. 5 minutes after the second failure
  3. 15 minutes after the third failure
  4. 30 minutes after the fourth failure
  5. 1 hour after the fifth failure
After 5 failed attempts, we’ll stop retrying and you’ll need to check the events in your dashboard.

Security

To verify the authenticity of webhooks, you should:
  1. Check the timestamp in the signature header to ensure the webhook is recent
  2. Verify the signature using your webhook secret
Example verification code:
import hmac
import hashlib
import time

def verify_webhook_signature(payload, signature_header, webhook_secret):
    # Extract timestamp and signature
    timestamp, signature = signature_header.split(',')
    timestamp = int(timestamp.split('=')[1])
    signature = signature.split('=')[1]
    
    # Check if the webhook is recent (within 5 minutes)
    if time.time() - timestamp > 300:
        return False
    
    # Calculate expected signature
    expected_signature = hmac.new(
        webhook_secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, expected_signature)