Axle sends webhook notifications when data is uploaded and validated, and when Rose investigations complete. This document explains how to receive and validate these webhooks.
Setup
- Login to Axle
- Within settings, navigate to the Webhooks tab
- Submit the form to create your webhook by pressing "Create Webhook"
Webhook Details
Webhook Documentation
Overview
The system provides webhook notifications for critical events in transaction monitoring and investigation processing. Webhooks enable real-time updates to external systems when key events occur.
Webhook Events
Transaction Upload Events
TRANSACTION_UPLOAD_PROCESSING
TRANSACTION_UPLOAD_PROCESSINGTriggered when transaction file download from storage is successful.
Payload:
{
"event_type": "transaction_upload.processing",
"session_id": "b9e748f2-a600-4e6b-96b9-991a9399bb60",
"organization_id": "da4ad981-739b-4524-8c99-f3d339ca1ae7",
"message": "We downloaded your transaction file."
}TRANSACTION_UPLOAD_VALIDATED
TRANSACTION_UPLOAD_VALIDATEDTriggered after successful validation of transaction data.
Payload:
{
"event_type": "transaction_upload.validated",
"session_id": "b9e748f2-a600-4e6b-96b9-991a9399bb60",
"organization_id": "da4ad981-739b-4524-8c99-f3d339ca1ae7",
"message": "All transactions were successfully validated"
}TRANSACTION_UPLOAD_COMPLETED
TRANSACTION_UPLOAD_COMPLETEDTriggered when transactions are successfully inserted into the database.
Payload:
{
"event_type": "transaction_upload.completed",
"session_id": "b9e748f2-a600-4e6b-96b9-991a9399bb60",
"organization_id": "da4ad981-739b-4524-8c99-f3d339ca1ae7",
"message": "All of the transaction were successfully inserted."
}TRANSACTION_UPLOAD_FAILED
TRANSACTION_UPLOAD_FAILEDTriggered when transaction processing fails at any stage.
Payload:
{
"event_type": "transaction_upload.failed",
"organization_id": "da4ad981-739b-4524-8c99-f3d339ca1ae7",
"session_id": "b9e748f2-a600-4e6b-96b9-991a9399bb60",
"error_count": 1,
"errors": [
"Failed to insert transactions"
],
"description": "Transaction validation failed"
}Investigation Events
ALERT_ANALYSIS_COMPLETED
ALERT_ANALYSIS_COMPLETEDTriggered when Rose alert investigation analysis completes.
Payload:
{
"event_type": "alert.analysis_completed",
"timestamp": "2024-01-15T11:00:00.000000",
"organization_id": "da4ad981-739b-4524-8c99-f3d339ca1ae7",
"investigation_id": "387b41df-7b57-434c-8be8-064d3bb91fcd",
"alert_source_id": "alert-id-456",
"disposition": "ESCALATED"
}PDF Generation Events
PDF Generation events are only generated for ROSE PDFs. Baley PDF generation is a distinct endpoint that requires polling.
Rose PDF generation can occur synchronously or asynchronously (webhook delivery). Only asynchronous requests result in webhook event delivery. Synchronous vs asynchronous PDF generation is configured with a API request parameter and defaults to synchronous generation.
Rose PDF generation is generally relatively fast. It rarely takes more than 30 seconds to generate. If you have not received a success or failure webhook event after ~90 seconds, you should assume there is a webhook configuration problem and contact Axle support. When Rose PDF webhooks are properly configured for both success and failure PDF event types, you will receive either a success or failure event for every PDF generation request. We strongly recommend setting up webhooks for both PDF generation success and failure events to prevent silent failures. These are separate webhook events and must be configured separately.
ROSE_PDF_GENERATED
Triggered when a Rose PDF has been generated successfully. Payload carries the base64 encoded PDF plus metadata.
Payload:
{
"event_type": "rose.pdf.generated",
"investigation_id": "cf4a5a38-4d8b-4ff5-964a-77f0c0fd5534",
"organization_id": "da4ad981-739b-4524-8c99-f3d339ca1ae7",
"timestamp": "2024-01-15T11:00:00.000000",
"pdf": {
"content": "<base64-encoded bytes>",
"content_type": "application/pdf",
"filename": "rose-alert-cf4a5a38-4d8b-4ff5-964a-77f0c0fd5534.pdf"
}
}ROSE_PDF_GENERATION_FAILED
Triggered when PDF rendering fails after a PDF generation request was accepted (202 response code). On occasion, we are unable to generate a PDF as requested. In these cases we will trigger a ROSE_PDF_GENERATION_FAILED event when rendering fails after the request was accepted in asynchronous webhook delivery mode.
Payload:
{
"event_type": "rose.pdf.generation_failed",
"investigation_id": "cf4a5a38-4d8b-4ff5-964a-77f0c0fd5534",
"organization_id": "da4ad981-739b-4524-8c99-f3d339ca1ae7",
"timestamp": "2024-01-15T11:00:00.000000",
"error": {
"message": "Human-readable explanation"
}
}Webhook Configuration
Webhooks are configured per organization and event type. Each organization can have multiple webhook endpoints for the same event.
Webhook Security
Authentication:
- Each webhook request includes HMAC-SHA256 signature in
X-Signatureheader - Signature computed using organization's webhook secret
- Verify signature before processing webhook payload
Headers:
X-Signature: signature...
X-Delivery-ID: unique-delivery-id
User-Agent: Hikari-Webhook/1.0
Content-Type: application/json
Retry Logic
- Failed webhook deliveries are retried with exponential backoff and jitter
- Maximum 5 retry attempts
Response Requirements
- Webhook endpoint must return HTTP 2xx status code within 10 seconds
- Non-2xx responses or timeouts trigger retry logic
- Response body is ignored
Error Handling
Common Failure Scenarios
-
Validation Failures
- Invalid JSON format
- Missing required fields
- Entity reference mismatches
- Data type violations
-
Database Insertion Failures
- Duplicate transaction IDs
- Foreign key violations
- Connection timeouts
Security
- All webhooks are signed using HMAC-SHA256
- Verify signature before processing payload
- Only HTTPS endpoints with TLS 1.2 or higher are supported
- Keep your webhook secret secure
Need help? Contact Axle.
Implementation Examples
import hmac
import hashlib
import json
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "your-webhook-secret"
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(
f"sha256={expected}",
signature
)
@app.route('/webhook', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Signature')
if not signature:
abort(401)
if not verify_signature(request.data, signature, WEBHOOK_SECRET):
abort(401)
event = json.loads(request.data)
event_type = event.get('event_type')
if event_type == 'transaction_upload.completed':
handle_upload_complete(event)
elif event_type == 'alert.analysis_completed':
handle_analysis_complete(event)
elif event_type == 'transaction_upload.failed':
handle_upload_failure(event)
return '', 200
def handle_upload_complete(event):
print(f"Transactions uploaded: {event['transactions_inserted']}")
def handle_analysis_complete(event):
print(f"Investigation {event['investigation_id']} completed: {event['disposition']}")
def handle_upload_failure(event):
print(f"Upload failed: {event['errors']}")