Axle sends webhook notifications when investigation statuses are updated. 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
Event Types
Currently only supports: investigation.updated
Status Fields
# Processing State
- COMPLETED
- FAILED
# Approval State
- AI_CLEARED
- AI_ESCALATEDSample Payload
{
"event_type": "investigation.updated",
"investigation_id": "14c08b5d-e32c-46d7-ba5e-156bd543eefa",
"organization_id": "766b0fc0-bfe2-4032-a510-ccd068a0c160",
"investigation_type": "individual_investigation", // OR "business_investigation" OR "transaction_monitoring_investigation"
"status": {
"approval_state": "AI_CLEARED",
"processing_state": "COMPLETED"
},
"timestamp": "2025-02-07T23:24:32.436517188"
}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 webhook secret secure
Need help? Contact Axle support.
Implementation Examples
from fastapi import FastAPI, Request, HTTPException
import hmac
import hashlib
app = FastAPI()
# Load this securely in production
WEBHOOK_SECRET = "your_webhook_secret"
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
"""Verify webhook signature"""
computed = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, computed)
@app.post("/webhook")
async def webhook_handler(request: Request):
payload = await request.body()
signature = request.headers.get("X-Signature")
if not signature:
raise HTTPException(status_code=400, detail="Missing signature")
if not verify_signature(payload, signature, WEBHOOK_SECRET):
raise HTTPException(status_code=401, detail="Invalid signature")
# Process webhook payload
data = await request.json()
return {"status": "success"}package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"io"
"log"
"net/http"
)
const (
// In production, load this securely from environment variables or secret management
WebhookSecret = "your_webhook_secret"
)
// VerifySignature verifies the HMAC signature of the webhook payload
func VerifySignature(payload []byte, signature string, secret string) bool {
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
expectedSignature := hex.EncodeToString(h.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expectedSignature))
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Read the request body
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading request body", http.StatusBadRequest)
return
}
defer r.Body.Close()
// Get the signature from headers
signature := r.Header.Get("X-Signature")
if signature == "" {
http.Error(w, "Missing signature", http.StatusBadRequest)
return
}
// Verify the signature
if !VerifySignature(body, signature, WebhookSecret) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
// Process the webhook payload here
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "success"}`))
}
func main() {
http.HandleFunc("/webhook", webhookHandler)
log.Println("Server starting on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}