Skip to main content
Consider carefully before enabling. Due to the constraints of on-chain transactions, card authorizations operate within a tight latency window. Adding real-time authorization introduces additional latency that may result in increased timeout rates and declined transactions. We recommend enabling this feature only if your use case requires real-time decisioning that cannot be achieved through Bridge’s built-in authorization controls.

Overview

Real-time authorization allows you to make custom approval or decline decisions during card transactions. When a cardholder attempts to make a purchase, Bridge calls your webhook endpoint synchronously, enabling you to apply your own business logic before the transaction is approved or declined. This gives you fine-grained control over card spending, such as:
  • Custom spending limits and velocity controls
  • Merchant category restrictions
  • Geographic restrictions
  • Fraud detection and risk scoring
Real-time authorization is optional. If not configured, Bridge will authorize transactions based on available balance and other built-in controls.

Quick Start

  1. Set up an HTTPS endpoint that can handle POST requests
  2. Configure your webhook URL via the Bridge API
  3. Implement signature verification for security
  4. Return authorization decisions within the timeout window

How It Works

When a card is used for a purchase, the following flow occurs:
  1. Card network receives transaction: The cardholder initiates a purchase
  2. Bridge receives authorization request: The card network sends the authorization to Bridge
  3. Bridge calls your webhook: Bridge forwards the authorization details to your configured endpoint
  4. Your decision: Your webhook returns an approve or decline decision
  5. Bridge responds to network: Bridge relays your decision back to the card network
  6. Transaction completes: The purchase is approved or declined
Cardholder → Card Network → Bridge → Your Webhook → Bridge → Card Network → Merchant
Even if your webhook returns approved: true, the transaction may still be declined if the total end-to-end latency exceeds the card network’s timeout. You will be informed of the final authorization result through the regular card transaction webhooks.

Webhook Request

When a card transaction requires authorization, Bridge sends a POST request to your configured webhook URL.

Request Headers

HeaderDescription
X-Webhook-SignatureRSA signature for payload verification
Content-Typeapplication/json

Request Body Example

{
  "event_id": "dc85e7d3-dce7-49ca-8023-2a74c65eca29",
  "api_version": "v0",
  "timestamp": "2025-06-09T15:30:00.000Z",
  "data": {
    "authorization_id": "06e774a7-8a54-48f8-b5b7-4c266403f560",
    "auth_type": "auth",
    "partial_supported": false,
    "network": "visa",
    "international": false,
    "original_authorization_id": null,
    "transaction_id": "00b4b744-375d-499f-824f-db1dcca995dd",
    "account": {
      "last_4": "5198"
    },
    "currency": "usd",
    "amount": "-25.50",
    "billing_amount": "-25.50",
    "cashback_amount": "0.00",
    "local_transaction_details": {
      "amount": "-25.50",
      "currency": "usd",
      "exchange_rate": "1.0000"
    },
    "merchant": {
      "description": "COFFEE SHOP",
      "postal_code": "94025",
      "state": "CA",
      "country": "USA",
      "category": "fast_food_restaurants",
      "category_code": "5814"
    },
    "entry_method": "chip",
    "card_present": true,
    "recurring": false,
    "verification_data": {
      "cvv_check": "match",
      "address_check": "match",
      "address_postal_code_check": "match",
      "pin_check": "verified",
      "three_d_secure_check": "authenticated"
    },
    "wallet": null,
    "card_account_id": "5bfb3f83-ebf2-482d-a215-4c3c5bf99c64",
    "customer_id": "4690efc8-4d35-4bfc-8f89-425f0a4a115b",
    "created_at": "2025-06-09T15:30:00.000Z"
  }
}

Field Reference

Top-level Fields

FieldTypeDescription
event_idstring (UUID)Unique identifier for this authorization request
api_versionstringAPI version of the payload schema
timestampstring (ISO-8601)UTC timestamp when the event was generated
dataobjectAuthorization data (see below)

Data Object

FieldTypeDescription
authorization_idstring (UUID)Unique identifier for this authorization
auth_typestringType of authorization: auth or incremental_auth
partial_supportedbooleanWhether the merchant supports partial authorization
networkstringCard network: visa, mastercard, discover, amex
internationalbooleanWhether this is a cross-border transaction
original_authorization_idstring/nullReference to original auth for incremental authorizations
transaction_idstring (UUID)Card transaction identifier
account.last_4stringLast 4 digits of the card PAN
currencystringTransaction currency (ISO-4217)
amountstringTransaction amount (negative for purchases)
billing_amountstringAmount in billing currency
cashback_amountstringRequested cashback amount
card_account_idstring (UUID)Bridge card account identifier
customer_idstring (UUID)Bridge customer identifier
created_atstring (ISO-8601)When the authorization was initiated

Merchant Object

FieldTypeDescription
descriptionstring/nullMerchant name/descriptor
postal_codestring/nullMerchant postal/ZIP code
statestring/nullMerchant state/province code
countrystring/nullMerchant country code
categorystring/nullMCC category name
category_codestringMerchant Category Code (MCC)

Local Transaction Details

FieldTypeDescription
amountstringAmount in merchant’s local currency
currencystringMerchant’s local currency code
exchange_ratestringConversion rate from local to billing currency

Verification Data

FieldTypeValues
cvv_checkstringmatch, mismatch, not_provided
address_checkstringmatch, mismatch, not_provided
address_postal_code_checkstringmatch, mismatch, not_provided
pin_checkstring/nullverified, failed, blocked, locked, not_set, no_pin_passed
three_d_secure_checkstring/nullattempt_acknowledged, authenticated, failed, required

Entry Method Values

ValueDescription
chipEMV chip transaction
contactlessNFC/contactless transaction
swipeMagnetic stripe transaction
manualManually keyed transaction
onlineE-commerce transaction
otherOther entry method

Wallet Values

ValueDescription
apple_payApple Pay
google_payGoogle Pay
samsung_paySamsung Pay
otherOther digital wallet
nullPhysical card (no wallet)

Webhook Response

Your webhook must return a JSON response with HTTP status code 200.

Response Headers

HeaderValue
Content-Typeapplication/json

Response Body

FieldRequiredTypeDescription
approvedYesbooleantrue to approve, false to decline
balanceNoobjectCurrent user balance on your platform
decision_reasonNostringHuman-readable reason for the decision

Response Examples

{
  "approved": true
}

Security

HTTPS Requirement

Your webhook endpoint must use HTTPS. HTTP endpoints are not supported.

Signature Verification

Every webhook request includes an RSA signature in the X-Webhook-Signature header using the format:
t=<timestamp>,v0=<base64-encoded-signature>
You should verify this signature to ensure request authenticity and freshness.

Verification Steps

  1. Extract the timestamp (t) and signature (v0) from the header
  2. Check that the timestamp is recent (reject events older than 10 minutes)
  3. Create the signed payload string: {timestamp}.{raw_request_body}
  4. Verify the RSA signature using Bridge’s public key
Bridge’s public key is provided when you configure your webhook endpoint. You can retrieve it via the webhook settings API.

Timeouts and Fallback Behavior

Timeout Configuration

The default timeout for webhook responses is 500ms. If your webhook doesn’t respond within this window:
  1. Bridge stops waiting for your response
  2. The configured fallback behavior is applied
  3. The transaction continues based on fallback settings

Fallback Modes

ModeDescription
DECLINEAutomatically decline the transaction (default)
APPROVEAutomatically approve the transaction
The default fallback mode is DECLINE. This means if your webhook is slow or unavailable, transactions will be declined. Consider your use case carefully before changing to APPROVE.

Configure Webhook Settings

Retrieve Current Settings

curl --location 'https://api.bridge.xyz/v0/developer/cards/auth_webhook_settings' \
  --header 'Api-Key: <your-api-key>'
Response:
{
  "enabled": true,
  "webhook_url": "https://your-domain.com/webhooks/card-auth",
  "timeout_ms": 500,
  "fallback_mode": "DECLINE",
  "public_key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}

Create Webhook Configuration

curl --location --request POST 'https://api.bridge.xyz/v0/developer/cards/auth_webhook_settings' \
  --header 'Api-Key: <your-api-key>' \
  --header 'Idempotency-Key: <idempotency-key>' \
  --header 'Content-Type: application/json' \
  --data '{
    "enabled": true,
    "webhook_url": "https://your-domain.com/webhooks/card-auth"
  }'

Update Webhook Configuration

Update webhook URL:
curl --location --request PUT 'https://api.bridge.xyz/v0/developer/cards/auth_webhook_settings' \
  --header 'Api-Key: <your-api-key>' \
  --header 'Content-Type: application/json' \
  --data '{
    "enabled": true,
    "webhook_url": "https://your-domain.com/webhooks/card-auth-v2"
  }'
Disable webhook:
curl --location --request PUT 'https://api.bridge.xyz/v0/developer/cards/auth_webhook_settings' \
  --header 'Api-Key: <your-api-key>' \
  --header 'Content-Type: application/json' \
  --data '{
    "enabled": false
  }'

Webhook Handler Integration

Here’s a complete example webhook handler in Python:
from flask import Flask, request, jsonify
import json
import base64
import time
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding

app = Flask(__name__)

# Replace with your actual public key from webhook settings
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) -> bool:
    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 False
        
        # Check timestamp (reject events older than 10 minutes)
        current_time = int(time.time() * 1000)
        if current_time - int(timestamp) > 600000:
            return False
        
        # 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 True
    except Exception:
        return False

@app.route('/webhooks/card-auth', methods=['POST'])
def handle_card_authorization():
    try:
        payload = request.get_data()
        signature_header = request.headers.get('X-Webhook-Signature')
        
        if not signature_header:
            return jsonify({'error': 'Missing signature header'}), 400
        
        # Verify signature
        if not verify_webhook_signature(payload, signature_header, WEBHOOK_PUBLIC_KEY):
            return jsonify({'error': 'Invalid signature'}), 400
        
        # Parse webhook data
        webhook_data = json.loads(payload)
        auth_data = webhook_data['data']
        
        # Extract authorization details
        amount = abs(float(auth_data['amount']))
        customer_id = auth_data['customer_id']
        merchant = auth_data['merchant']
        
        # Implement your business logic here
        # Examples:
        # - Check user balance
        # - Validate merchant category
        # - Check spending limits
        # - Run fraud detection
        
        # Approve the transaction
        return jsonify({
            'approved': True
        })
        
    except Exception as e:
        print(f"Webhook error: {e}")
        return jsonify({'error': 'Internal server error'}), 500

if __name__ == '__main__':
    app.run(port=3000)
Test your webhook integration thoroughly in the sandbox environment before going live. Simulate various scenarios including timeouts, errors, and edge cases.