AviationAlley pushes real-time events to your HTTPS endpoint. Subscribe per-center in Settings → Integrations; you receive a signing secret once at creation. Every delivery is HMAC-SHA256 signed so you can verify it came from us.
POST, JSON body, 5-second timeout.X-AviationAlley-Signature (hex HMAC-SHA256 of the raw body), X-AviationAlley-Event (event type), X-AviationAlley-Delivery (unique delivery id).2xx within 5s. Any other status (or a timeout) is treated as a failure and retried.Envelope
{
"id": "del_8f2c…", // matches X-AviationAlley-Delivery
"type": "booking.created", // matches X-AviationAlley-Event
"centerId": "uuid",
"createdAt": "2026-05-31T14:03:22.000Z",
"data": { /* event-specific payload */ }
}Compute HMAC-SHA256(secret, rawRequestBody) as hex and compare it (constant-time) to X-AviationAlley-Signature. Always verify against the raw bytes, before any JSON parsing/re-serialization.
import crypto from "node:crypto";
function verify(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody) // the raw request body, before JSON.parse
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader),
);
}
// Express handler (use express.raw so the body stays unparsed):
app.post("/webhooks/aviationalley", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.header("X-AviationAlley-Signature");
if (!verify(req.body, sig, process.env.AA_WEBHOOK_SECRET)) {
return res.status(401).end();
}
const event = JSON.parse(req.body.toString());
// event.type, event.data, event.centerId, event.createdAt
res.status(200).end();
});import hashlib, hmac
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header)
# Flask:
@app.post("/webhooks/aviationalley")
def hook():
sig = request.headers.get("X-AviationAlley-Signature", "")
if not verify(request.get_data(), sig, os.environ["AA_WEBHOOK_SECRET"]):
abort(401)
event = request.get_json()
return "", 200require "openssl"
def verify(raw_body, signature_header, secret)
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, raw_body)
Rack::Utils.secure_compare(expected, signature_header)
end
# Rails:
def aviationalley
raw = request.raw_post
head :unauthorized and return unless verify(raw, request.headers["X-AviationAlley-Signature"], ENV["AA_WEBHOOK_SECRET"])
event = JSON.parse(raw)
head :ok
end<?php
function verify(string $rawBody, string $sigHeader, string $secret): bool {
$expected = hash_hmac("sha256", $rawBody, $secret);
return hash_equals($expected, $sigHeader);
}
$raw = file_get_contents("php://input");
$sig = $_SERVER["HTTP_X_AVIATIONALLEY_SIGNATURE"] ?? "";
if (!verify($raw, $sig, getenv("AA_WEBHOOK_SECRET"))) {
http_response_code(401);
exit;
}
$event = json_decode($raw, true);
http_response_code(200);package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func verify(rawBody []byte, sigHeader, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(rawBody)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(sigHeader))
}39 event types across 14 domains. Subscribe to specific events or * for all.
bidding.awards_publishedbooking.createdbooking.updatedbooking.cancelledbooking.completedcompliance.due_sooncompliance.overduedat.test.scheduleddat.test.resulteddat.random_drawfa_training.scheduledfa_training.completedfa_training.expiringfa_training.expiredinvoice.createdinvoice.paidinvoice.voidedmel.deferral.openedmel.deferral.expiringmel.deferral.expiredquote.createdquote.acceptedquote.declinedreserve.assignedreserve.called_outreserve.no_contactsafety.report.submittedsafety.report.closedsafety.finding.closedstage_check.scheduledstage_check.passedstage_check.failedtrainee.createdtrainee.updatedtrip_trade.requestedtrip_trade.approvedworkorder.createdworkorder.updatedworkorder.completed