← API reference

Webhooks

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.

Delivery contract

  • Method: HTTPS POST, JSON body, 5-second timeout.
  • Headers: X-AviationAlley-Signature (hex HMAC-SHA256 of the raw body), X-AviationAlley-Event (event type), X-AviationAlley-Delivery (unique delivery id).
  • Retries: failed deliveries retry with exponential backoff (5m, 30m, 2h, 8h — up to 5 attempts).
  • Success: respond 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 */ }
}

Verify the signature

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.

Node.js
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();
});
Python
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 "", 200
Ruby
require "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
<?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);
Go
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))
}

Event vocabulary

39 event types across 14 domains. Subscribe to specific events or * for all.

bidding

  • bidding.awards_published

booking

  • booking.created
  • booking.updated
  • booking.cancelled
  • booking.completed

compliance

  • compliance.due_soon
  • compliance.overdue

dat

  • dat.test.scheduled
  • dat.test.resulted
  • dat.random_draw

fa training

  • fa_training.scheduled
  • fa_training.completed
  • fa_training.expiring
  • fa_training.expired

invoice

  • invoice.created
  • invoice.paid
  • invoice.voided

mel

  • mel.deferral.opened
  • mel.deferral.expiring
  • mel.deferral.expired

quote

  • quote.created
  • quote.accepted
  • quote.declined

reserve

  • reserve.assigned
  • reserve.called_out
  • reserve.no_contact

safety

  • safety.report.submitted
  • safety.report.closed
  • safety.finding.closed

stage check

  • stage_check.scheduled
  • stage_check.passed
  • stage_check.failed

trainee

  • trainee.created
  • trainee.updated

trip trade

  • trip_trade.requested
  • trip_trade.approved

workorder

  • workorder.created
  • workorder.updated
  • workorder.completed