Skip to Content
B2B APIWebhook Endpoints

Webhooks

Webhooks provide a way to receive real-time notifications when specific events occur within the Orca system. This allows your application to automatically react to events such as transactions being screened or risk assessments being completed.

We recommend that Webhooks should be used for the Time-Sensitive Transaction Monitoring.

Overview

Webhooks are HTTP callbacks that deliver data to your servers when events happen in the Orca system. They eliminate the need for continuous polling and allow your application to be notified immediately when events occur.

Event Types

The following event types are currently supported:

Event TypeDescription
transaction.screenedTriggered when a transaction has completed risk screening
merchant.screenedTriggered when a merchant has completed risk screening

Managing Webhook Subscriptions

Create Webhook Subscription

Register a new webhook endpoint to receive event notifications.

Endpoint

POST /v1/webhooks

Request Schema

FieldTypeRequiredDescription
urlstringYesThe URL that will receive webhook events
eventsstring[]YesArray of event types to subscribe to
secretstringYesSecret used to sign webhook payloads for verification

Example Request

{ "url": "https://example.com/webhooks/orca-events", "events": [ "transaction.screened", "merchant.screened" ], "secret": "whsec_8fe59a8886bb4a31a54339c25a57c286" }

Example Response

{ "status": "success", "subscription": { "id": "wsub_123456789", "url": "https://example.com/webhooks/orca-events", "events": [ "transaction.screened", "merchant.screened" ] } }

Responses

  • 200: Webhook subscription created successfully
  • 400: Missing required fields or invalid format
  • 500: Internal server error

List Webhook Subscriptions

Retrieve all active webhook subscriptions for your organization.

Endpoint

GET /v1/webhooks

Example Response

{ "status": "success", "subscriptions": [ { "id": "wsub_123456789", "url": "https://example.com/webhooks/orca-events", "events": [ "transaction.screened", "merchant.screened" ] }, { "id": "wsub_987654321", "url": "https://example.com/webhooks/transaction-events", "events": [ "transaction.screened" ] } ] }

Responses

  • 200: Webhook subscriptions retrieved successfully
  • 500: Internal server error

Verifying Webhooks

To ensure that webhook requests are coming from Orca and haven’t been tampered with, each webhook request includes a signature. The signature is created using the secret you provided when setting up the webhook.

Signature Verification

The webhook signature is included in the X-Orca-Signature header of the webhook request with the following format:

t=1678364102,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e1e13a75

To verify the signature:

  1. Split the header on commas to get the timestamp and signature
  2. Extract the timestamp after “t=”
  3. Extract the signature after “v1=”
  4. Combine the timestamp and raw response body with a period: ${timestamp}.${rawBody}
  5. Create an HMAC-SHA256 signature using your webhook secret
  6. Compare the expected signature with the received signature
  7. Optionally verify the timestamp is recent (within 5 minutes)

Example implementation in Typescript:

import crypto from 'crypto'; /** * Verifies the signature of an incoming webhook request * @param req The HTTP request object * @param secret The webhook secret for signature verification * @returns boolean indicating if the signature is valid */ function verifyWebhookSignature( req: any, secret: string ): boolean { try { // Get the signature from headers const signature = req.headers['x-orca-signature']; if (!signature) { console.error('Missing X-Orca-Signature header'); return false; } // Get raw body as string const rawBody = typeof req.body === 'string' ? req.body : Buffer.isBuffer(req.body) ? req.body.toString('utf8') : JSON.stringify(req.body); // Parse signature components const [timeComponent, signatureComponent] = signature.split(','); const timestamp = timeComponent.replace('t=', ''); const receivedSignature = signatureComponent.replace('v1=', ''); // Verify timestamp is recent (within 5 minutes) const now = Date.now(); const timestampNum = parseInt(timestamp, 10); // Compare using milliseconds (300000ms = 5 minutes) if (Math.abs(now - timestampNum) > 300000) { console.error('Webhook timestamp too old'); return false; } // Create the message to verify const message = `${timestamp}.${rawBody}`; // Create expected signature const hmac = crypto.createHmac('sha256', secret); hmac.update(message); const expectedSignature = hmac.digest('hex'); // Compare signatures return crypto.timingSafeEqual( Buffer.from(receivedSignature), Buffer.from(expectedSignature) ); } catch (error) { console.error('Error verifying webhook signature:', error); return false; } }

Example implementation in Python:

import hmac import hashlib import time import base64 import json from flask import Flask, request, abort app = Flask(__name__) def verify_webhook_signature(signature, raw_body, secret): """Verify the webhook signature against the raw body.""" try: # Parse signature components time_component, signature_component = signature.split(',') timestamp = time_component.replace('t=', '') received_signature = signature_component.replace('v1=', '') # Verify timestamp is recent (within 5 minutes) now = int(time.time() * 1000) # Current time in milliseconds timestamp_num = int(timestamp) if abs(now - timestamp_num) > 300000: # 5 minutes in milliseconds print("Webhook timestamp too old") return False # Create the message to verify message = f"{timestamp}.{raw_body}" # Create expected signature computed_hmac = hmac.new( secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256 ) expected_signature = computed_hmac.hexdigest() # Compare signatures using constant time comparison return hmac.compare_digest(received_signature, expected_signature) except Exception as e: print(f"Error verifying webhook signature: {e}") return False @app.route('/webhook', methods=['POST']) def webhook(): # Get the raw request body raw_body = request.get_data(as_text=True) # Get signature from header signature = request.headers.get('X-Orca-Signature') # Verify signature before processing if not verify_webhook_signature( signature, raw_body, secret="your_webhook_secret" ): abort(401, "Invalid signature") # Process the webhook payload data = json.loads(raw_body) print(f"Received verified webhook event: {data.get('event')}") return "Webhook received", 200 if __name__ == "__main__": app.run(debug=True)

Webhook payloads vary depending on the event type but follow a consistent structure.

Example Payload: transaction.screened

{ "event": "transaction.screened", "timestamp": 1734167723000, "data": { "id": "TXN123456", "riskLevel": "high", "recommendedAction": "REVIEW", "triggered": [ { "id": "RULE123", "name": "High Value Transaction", "level": "high" } ] } }

Best Practices

  1. Acknowledge webhooks quickly: Return a 2xx response as soon as possible.
  2. Process webhooks asynchronously: Handle the webhook data processing in the background.
  3. Implement idempotency: Design your webhook handler to be idempotent to avoid issues with duplicate events.
  4. Always verify signatures first: Verify the signature of each webhook request before parsing the body to ensure authenticity.
  5. Set up a retry mechanism: Be prepared to handle cases where your webhook endpoint is temporarily unavailable.
Last updated on