Bridge webhooks allow you to receive real-time notifications when events occur in your Bridge account. This guide covers creating, implementing, testing, and enabling webhooks using Bridge’s REST API.

Prerequisites

  • A Bridge account with API access
  • Bridge API credentials (API key)
  • HTTPS endpoint with valid X.509 certificate
  • Development environment with your preferred language (Ruby, Node.js, Python, or Go)

Step 1: Create a New Webhook

First, create a webhook endpoint using the Bridge API. The webhook will be created in disabled state initially.
Request
curl -X POST "https://api.bridge.xyz/webhooks" \
  -H "Authorization: Bearer your_bridge_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-domain.com/webhooks/bridge",
    "events": ["customer.created", "customer.updated"]
  }'
Response
{
  "id": "webhook_abc123",
  "status": "disabled",
  "url": "https://your-domain.com/webhooks/bridge",
  "events": ["customer.created", "customer.updated"],
  "created_at": "2024-01-15T10:30:00Z"
}
Save the webhook_id from the response - you’ll need it for testing and enabling the webhook.

Step 2: Implement the Webhook Handler

Create an endpoint that can receive and process Bridge webhook events with proper timestamp validation, refer Webhook Event Signature Verification. Bridge webhook signatures use the format: X-Webhook-Signature: t=<timestamp>,v0=<base64-encoded-signature>
from flask import Flask, request, jsonify
from typing import Dict, Any, Optional, Union
import json
import base64
import time
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.exceptions import InvalidSignature

app = Flask(__name__)

class WebhookEvent:
    def __init__(self, data: Dict[str, Any]):
        self.api_version: str = data['api_version']
        self.event_id: str = data['event_id']
        self.event_category: str = data['event_category']
        self.event_type: str = data['event_type']
        self.event_object: Dict[str, Any] = data['event_object']
        self.event_object_changes: Optional[Dict[str, Any]] = data.get('event_object_changes')
        self.event_created_at: str = data['event_created_at']

class SignatureVerificationResult:
    def __init__(self, is_valid: bool, error: Optional[str] = None):
        self.is_valid = is_valid
        self.error = error

WEBHOOK_PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
your_webhook_public_key_here
-----END PUBLIC KEY-----"""

def verify_webhook_signature(payload: bytes, signature_header: str, public_key_pem: str) -> SignatureVerificationResult:
    try:
        # Parse signature header
        signature_parts = signature_header.split(',')
        timestamp = next((part.split('=', 1)[1] for part in signature_parts if part.startswith('t=')), None)
        signature = next((part.split('=', 1)[1] for part in signature_parts if part.startswith('v0=')), None)
        
        if not timestamp or not signature:
            return SignatureVerificationResult(False, 'Missing timestamp or signature')
        
        # Check timestamp (reject events older than 10 minutes)
        current_time = int(time.time() * 1000)
        if current_time - int(timestamp) > 600000:
            return SignatureVerificationResult(False, 'Timestamp too old')
        
        # Create signed payload
        signed_payload = f"{timestamp}.{payload.decode()}"
        
        # Verify signature
        public_key = serialization.load_pem_public_key(public_key_pem.encode())
        signature_bytes = base64.b64decode(signature)
        
        public_key.verify(
            signature_bytes,
            signed_payload.encode(),
            padding.PKCS1v15(),
            hashes.SHA256()
        )
        return SignatureVerificationResult(True)
    except InvalidSignature:
        return SignatureVerificationResult(False, 'Invalid signature')
    except Exception as e:
        return SignatureVerificationResult(False, f'Signature verification failed: {e}')

def handle_webhook_event(event: WebhookEvent) -> None:
    if event.event_type == 'customer.created':
        print(f"New customer created: {event.event_object.get('id')}")
    elif event.event_type == 'customer.updated':
        print(f"Customer updated: {event.event_object.get('id')}")
    elif event.event_type == 'transfer.created':
        print(f"Transfer created: {event.event_object.get('id')}")
    else:
        print(f"Unhandled event type: {event.event_type}")

@app.route('/webhooks/bridge', methods=['POST'])
def handle_webhook():
    payload = request.get_data()
    signature_header = request.headers.get('X-Webhook-Signature')
    
    if not signature_header:
        return jsonify({'error': 'Missing signature header'}), 400
    
    verification = verify_webhook_signature(payload, signature_header, WEBHOOK_PUBLIC_KEY)
    
    if not verification.is_valid:
        print(f"Signature verification failed: {verification.error}")
        return jsonify({'error': 'Invalid signature'}), 400
    
    try:
        event_data = json.loads(payload)
        event = WebhookEvent(event_data)
        handle_webhook_event(event)
        return jsonify({'received': True})
    except Exception as e:
        print(f"Failed to parse webhook event: {e}")
        return jsonify({'error': 'Invalid JSON'}), 400

if __name__ == '__main__':
    app.run(port=3000, debug=True)

Step 3: Test the Webhook

Before enabling your webhook, test it to ensure it’s working correctly.

Send a Test Event

# Send a test event to your webhook
curl -X POST "https://api.bridge.xyz/webhooks/{webhook_id}/send" \
  -H "Authorization: Bearer your_bridge_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "customer.created",
    "test_data": {
      "id": "test_customer_123",
      "email": "test@example.com"
    }
  }'

Check Webhook Logs

# View webhook delivery logs
curl -X GET "https://api.bridge.xyz/webhooks/{webhook_id}/logs" \
  -H "Authorization: Bearer your_bridge_api_key"

Get Webhook Events

# Retrieve upcoming events for the webhook
curl -X GET "https://api.bridge.xyz/webhooks/{webhook_id}/events" \
  -H "Authorization: Bearer your_bridge_api_key"

Step 4: Enable the Webhook

Once you’ve tested your webhook and confirmed it’s working, enable it to start receiving live events.
Request
curl -X PATCH "https://api.bridge.xyz/webhooks/{webhook_id}" \
  -H "Authorization: Bearer your_bridge_api_key" \
  -H "Content-Type: application/json" \
  -d '{"status": "active"}'
Response
{
  "id": "webhook_abc123",
  "status": "active",
  "url": "https://your-domain.com/webhooks/bridge",
  "events": ["customer.created", "customer.updated"],
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:35:00Z"
}
Your webhook is now active and will receive live events from Bridge!

Security Best Practices

  1. Always verify webhook signatures to ensure events are from Bridge
  2. Use HTTPS endpoints with valid certificates
  3. Store webhook secrets securely (use environment variables)
  4. Return 200 status quickly to avoid timeouts
  5. Implement idempotency to handle duplicate events
  6. Log webhook events for debugging and monitoring

Common Event Types

  • customer.created - New customer registration
  • customer.updated - Customer information changes
  • payment.succeeded - Successful payment processing
  • payment.failed - Failed payment attempt
  • subscription.created - New subscription
  • subscription.cancelled - Subscription cancellation

Troubleshooting

  • Check webhook logs for delivery status and error messages
  • Verify your endpoint URL is accessible and returns 200
  • Ensure signature verification is implemented correctly
  • Check for certificate issues on your HTTPS endpoint
  • Monitor response times to avoid timeout issues

Next Steps

  • Review the Webhooks documentation for more details
  • Implement proper error handling and retry logic
  • Set up monitoring and alerting for webhook failures
  • Consider implementing webhook replay functionality for critical events
This completes your webhook integration with Bridge. Your application will now receive real-time notifications for customer events and can respond accordingly.