API Reference
The Bluecrest partner API lets you push flight disruptions from your DCS, receive real-time webhook events as passengers self-serve, and query claim status programmatically. All endpoints live under https://api.bluecrestlab.com and require an X-API-Key header.
Overview
Base URL
Authentication
Pass your API key in every request as an X-API-Key header — not Authorization: Bearer. Sandbox keys are prefixed wp_sandbox_. Live keys use wp_live_. Register via the portal UI or call POST /v1/dev/register with your email and company name to self-issue a sandbox key programmatically.
Idempotency
All POST requests accept an idempotency_key field. Submitting the same key twice returns the original response without creating a duplicate — safe to retry on network failure.
# Include this header on every request X-API-Key: wp_sandbox_xxxxxxxxxxxxxxxxxxxxxxxx # Example authenticated request curl https://api.bluecrestlab.com/v1/partner/airline/disruptions \ -H "X-API-Key: wp_sandbox_xxxx…" \ -H "Content-Type: application/json"
Integration patterns
Choose the pattern that matches how your systems generate disruption data.
Airline Direct
/v1/partner/airline/*Your DCS pushes a disruption manifest to Bluecrest. Passengers are notified automatically and self-serve via the claim portal.
Best for airlines with a DCS that can make outbound HTTPS calls.
Kiosk
/v1/partner/kiosk/*Passengers initiate a claim themselves by scanning their boarding pass at an airport kiosk or via the web portal. No API call needed from your side.
Best for airlines without DCS integration, or as a fallback channel.
Booking Platform
/v1/partner/booking/*OTAs and booking platforms (Go7, Amadeus, Sabre) can submit disruptions on behalf of their airline partners and receive consolidated reporting across carriers.
Reach out to discuss early access.
case.* events and can query status with GET /v1/partner/claims/{claim_id}.Submit disruption
POST/v1/partner/airline/disruptionspassenger_id. Use those IDs in all subsequent compensation and rerouting calls for this disruption. Use an idempotency_key based on flight + date + type to safely retry on network failure without creating duplicates.The response includes a
portal_url — a branded passenger self-service link for this disruption. Passengers can use it to submit their own claim if the airline does not complete rerouting on their behalf. Any claims submitted via the portal generate case.created events on your webhook.Parameters
flight_numberstringrequiredIATA flight code (e.g. NK001)flight_datestringrequiredISO date YYYY-MM-DDorigin_iatastringrequired3-letter IATA origin airportdestination_iatastringrequired3-letter IATA destination airportdisruption_typestringrequired"CANCELLED" | "DELAYED" | "DIVERTED"scheduled_departurestringrequiredISO 8601 datetime (UTC)idempotency_keystringrequiredUnique key per disruption — format: {flight}-{date}-{type}passengers[].passenger_idstringrequiredYour stable ID for this passenger (max 64 chars, alphanumeric + hyphens recommended) — scoped to your tenant, echoed back, used in all subsequent callspassengers[].pnrstringrequiredBooking reference (5–8 chars)passengers[].last_namestringrequiredPassenger last name (uppercase)passengers[].first_namestringrequiredPassenger first namepassengers[].cabin_classstringrequired"ECONOMY" | "PREMIUM_ECONOMY" | "BUSINESS" | "FIRST"passengers[].ticket_numberstringoptional13-digit IATA ticket numberpassengers[].seat_numberstringoptionalSeat assignment (e.g. 14A)passengers[].nationalitystringoptionalISO 3166-1 alpha-3 country code (e.g. GBR)delay_minutesintegeroptionalGate-to-gate delay in minutes — required when disruption_type = "DELAYED" (e.g. 185)actual_departurestringoptionalActual gate departure UTC ISO 8601 — null if cancelledsource_systemstringoptionalOriginating system identifier for audit purposes (default: "AIRLINE_DCS")curl -X POST https://api.bluecrestlab.com/v1/partner/airline/disruptions \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"flight_number": "NK001",
"flight_date": "2026-05-01",
"origin_iata": "ARN",
"destination_iata": "LHR",
"disruption_type": "CANCELLED",
"scheduled_departure": "2026-05-01T08:30:00Z",
"idempotency_key": "NK001-20260501-CANCELLED",
"passengers": [
{
"passenger_id": "NA-PAX-001",
"pnr": "NK1234",
"last_name": "SMITH",
"first_name": "John",
"cabin_class": "ECONOMY"
},
{
"passenger_id": "NA-PAX-002",
"pnr": "NK1235",
"last_name": "JONES",
"first_name": "Sarah",
"cabin_class": "BUSINESS"
}
]
}'{
"disruption_id": "f800450b-20f5-4382-895c-9ecdef8ff5de",
"status": "PROCESSED",
"flight_number": "NK001",
"flight_date": "2026-05-01",
"disruption_type": "CANCELLED",
"passenger_count": 2,
"passengers": [
{ "passenger_id": "NA-PAX-001", "pnr": "NK1234" },
{ "passenger_id": "NA-PAX-002", "pnr": "NK1235" }
],
"portal_url": "https://claim.bluecrestlab.com/nk001-2026-05-01",
"idempotency_key": "NK001-20260501-CANCELLED",
"created_at": "2026-05-01T06:00:00Z"
}Idempotency
Always set idempotency_key to a value unique per disruption event — e.g. NK001-20260501-CANCELLED. Re-submitting the same key within 24 hours returns the original 201 response and does not create a duplicate disruption. Submitting the same key with different parameters returns 409.
Safe retry rules
5xx / network timeout — Retry with the same idempotency_key429 Rate limited — Back off and retry after a short delay409 Conflict — Already accepted — treat as success4xx (other) — Do not retry — fix the request firstGet disruption status
GET/v1/partner/airline/disruptions/{disruption_id}POST /disruptions response already includes a status field — call this endpoint if you need to re-check state later or poll until PROCESSED before making per-passenger calls.In the current sandbox, ingestion is synchronous: the POST response returns
status: "PROCESSED" immediately and processed_count equals passenger_count. No polling loop is required.Parameters
disruption_idUUID (string, path)requireddisruption_id from POST /disruptions responsecurl "https://api.bluecrestlab.com/v1/partner/airline/disruptions/{disruption_id}" \
-H "X-API-Key: YOUR_API_KEY"{
"disruption_id": "f800450b-20f5-4382-895c-9ecdef8ff5de",
"status": "PROCESSED",
"flight_number": "NK001",
"flight_date": "2026-05-01",
"origin_iata": "ARN",
"destination_iata": "LHR",
"disruption_type": "CANCELLED",
"delay_minutes": null,
"passenger_count": 2,
"processed_count": 2,
"created_at": "2026-05-01T06:00:00Z",
"updated_at": "2026-05-01T06:00:00Z"
}Get compensation eligibility
GET/v1/partner/airline/disruptions/{disruption_id}/passengers/{passenger_id}/compensationdisruption_id from the disruption create response and the passenger_id you submitted in the manifest. If you prefer passengers to self-serve their claim, share the portal_url from the POST /disruptions response instead — they will complete the flow via the branded portal and you will receive a case.created webhook.Parameters
disruption_idstring (path)requireddisruption_id from POST /disruptions responsepassenger_idstring (path)requiredpassenger_id you submitted in the manifestcurl "https://api.bluecrestlab.com/v1/partner/airline/disruptions/{disruption_id}/passengers/{passenger_id}/compensation" \
-H "X-API-Key: YOUR_API_KEY"{
"disruption_id": "f800450b-20f5-4382-895c-9ecdef8ff5de",
"passenger_id": "NA-PAX-001",
"pnr": "NK1234",
"eligible": true,
"regulation": "EC261/2004",
"compensation_tier": "TIER_3",
"compensation_amount_eur": 600.0,
"currency": "EUR",
"eligibility_reason": "Flight distance ARN–LHR exceeds 3500 km and cancellation notified fewer than 14 days before departure.",
"exclusions": [],
"status": "ELIGIBLE"
}Get rerouting options
GET/v1/partner/airline/disruptions/{disruption_id}/passengers/{passenger_id}/reroutingbooking_token and a booking_token_expires_at UTC timestamp — the token is valid for 30 minutes from generated_at. If the token expires before the passenger selects, call this endpoint again — the response always contains a fresh set of tokens. Options are ranked by earliest arrival, cabin-class parity with the original booking, and live seat availability. Business and premium passengers are offered cabin-equivalent alternatives first; downgrades appear at the bottom of the list.Parameters
disruption_idstring (path)requireddisruption_id from POST /disruptions responsepassenger_idstring (path)requiredpassenger_id you submitted in the manifestcurl "https://api.bluecrestlab.com/v1/partner/airline/disruptions/{disruption_id}/passengers/{passenger_id}/rerouting" \
-H "X-API-Key: YOUR_API_KEY"{
"disruption_id": "f800450b-20f5-4382-895c-9ecdef8ff5de",
"passenger_id": "NA-PAX-001",
"pnr": "NK1234",
"origin_iata": "ARN",
"destination_iata": "LHR",
"generated_at": "2026-05-01T06:05:00Z",
"options": [
{
"option_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"rank": 1,
"mode": "FLIGHT",
"provider": "BLUECREST",
"carrier": "AA",
"carrier_name": "American Airlines",
"service_number": "AA100",
"departure_at": "2026-05-02T08:00:00Z",
"arrival_at": "2026-05-02T11:30:00Z",
"origin_iata": "ARN",
"destination_iata": "LHR",
"cabin_class": "ECONOMY",
"stops": 0,
"total_duration_minutes": 210,
"score": 92,
"booking_token": "bc_tok1_f800450b-20f5-4382-895c-9ecdef8ff5de_3c2d1e4f-…",
"booking_token_expires_at": "2026-05-01T06:35:00Z"
}
]
}Select rerouting option
POST/v1/partner/airline/disruptions/{disruption_id}/passengers/{passenger_id}/rerouting/selectoption_id and booking_token from the rerouting options response. Returns 422 if the token has expired — call GET .../rerouting to refresh before retrying. The cost field in the response is the fare charged to the airline by the operating carrier. 0.0 indicates the carrier operates the replacement service themselves; non-zero amounts are invoiced to the airline separately and do not appear on the passenger's ticket.Parameters
disruption_idstring (path)requireddisruption_id from POST /disruptions responsepassenger_idstring (path)requiredpassenger_id you submitted in the manifestoption_idUUID (string)requiredoption_id from GET .../rerouting response — must be a valid UUID v4booking_tokenstringrequiredbooking_token from the chosen option — expires 30 min after generationcurl -X POST "https://api.bluecrestlab.com/v1/partner/airline/disruptions/{disruption_id}/passengers/{passenger_id}/rerouting/select" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"option_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"booking_token": "bc_tok1_f800450b-20f5-4382-895c-9ecdef8ff5de_3c2d1e4f-…"
}'{
"disruption_id": "f800450b-20f5-4382-895c-9ecdef8ff5de",
"passenger_id": "NA-PAX-001",
"option_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"booking_id": "7a3b9e2c-1d4f-5e6a-8b9c-0d1e2f3a4b5c",
"provider_ref": "X7KM42",
"state": "CONFIRMED",
"booking_type": "flight",
"mode": "FLIGHT",
"carrier": "AA",
"service_number": "AA100",
"departure_at": "2026-05-02T08:00:00Z",
"arrival_at": "2026-05-02T11:30:00Z",
"cost": 0.0,
"currency": "GBP",
"confirmed_at": "2026-05-01T06:08:00Z"
}Claim status
GET/v1/partner/claims/{claim_id}claim_id is provided in the case.created webhook payload. States progress from SUBMITTED → UNDER_REVIEW → APPROVED or REJECTED.Parameters
claim_idstring (path)requiredclaim_id from the case.created webhook payloadcurl "https://api.bluecrestlab.com/v1/partner/claims/{claim_id}" \
-H "X-API-Key: YOUR_API_KEY"{
"claim_id": "cl_sandbox_001",
"state": "APPROVED",
"passenger_pnr": "NK1234",
"flight_number": "NK001",
"disruption_type": "CANCELLED",
"amount_gbp": 350,
"payout_method": "bank_transfer",
"created_at": "2026-05-01T09:00:00Z",
"updated_at": "2026-05-01T11:30:00Z"
}Register webhook
POST/v1/partner/webhookssecret field in the registration response is your signing secret — it is shown exactly once and cannot be retrieved again. Store it securely alongside your API key before making any other call.To rotate a compromised secret, delete the webhook and re-register — a new secret is issued on each registration. There is no in-place rotation to avoid a gap in delivery coverage during the switchover.
Sandbox webhooks are catch-all — every event type is delivered to your registered URL regardless of which scenario is active. Per-event filtering is available in production.
Parameters
urlstringrequiredHTTPS endpoint to receive eventsdescriptionstringoptionalHuman-readable labelcurl -X POST https://api.bluecrestlab.com/v1/partner/webhooks \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourdomain.com/bluecrest/events",
"description": "Production webhook"
}'{
"webhook_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"url": "https://yourdomain.com/bluecrest/events",
"secret": "whsec_xxxxxxxxxxxxxxxxxx",
"message": "Use the secret to verify webhook signatures via HMAC-SHA256."
}Event types
disruption.ingestedDisruption manifest accepted — one event per POST /disruptions call, passengers array included (Airline Direct)disruption.checkedPassenger checked in via kiosk — eligibility computedevidence.confirmedBoarding pass or supporting evidence verifiedcase.createdCompensation claim created for a passengercase.escalatedIssue escalated to airline staffbooking.flight_confirmedReplacement flight booking confirmed (Airline Direct)booking.hotel_confirmedHotel accommodation booking confirmedbooking.transport_confirmedGround transport booking confirmedcompensation.resultCompensation payout processed — amount and method in payloadcase.evidence_added(coming soon) Additional evidence added to an existing claimcase.approved(coming soon) Claim approved — payout initiatedcase.rejected(coming soon) Claim rejected — reason included in payloadcase.completed(coming soon) Full passenger journey resolved and closedExample payloads
Every event is delivered as a POST to your registered URL with a JSON body. All payloads share the same envelope: event (event type string), event_id (unique delivery ID), created_at (ISO 8601 UTC), and data (event-specific object). Use X-Bluecrest-Delivery to deduplicate retries — store it and discard deliveries you have already processed. Respond with any 2xx status to acknowledge.
Granularity: disruption.ingested fires once per manifest — a single event containing all passengers, matching the POST /disruptions call that triggered it. All downstream events (booking.flight_confirmed, case.created, etc.) fire once per passenger as each action occurs.
# --- disruption.ingested (fires once per POST /disruptions call, not per passenger) ---
POST https://yourdomain.com/bluecrest/events
Content-Type: application/json
X-Bluecrest-Signature: 3d9f2c1a8e...
X-Bluecrest-Event: disruption.ingested
X-Bluecrest-Delivery: d1e2f3a4-b5c6-7890-abcd-ef1234567890
{
"event": "disruption.ingested",
"event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"created_at": "2026-05-01T09:10:00Z",
"data": {
"disruption_id": "f800450b-20f5-4382-895c-9ecdef8ff5de",
"flight_number": "NK001",
"flight_date": "2026-05-01",
"origin_iata": "ARN",
"destination_iata": "LHR",
"disruption_type": "CANCELLED",
"passenger_count": 2,
"passengers": [
{ "passenger_id": "NA-PAX-001", "pnr": "NK1234" },
{ "passenger_id": "NA-PAX-002", "pnr": "NK1235" }
]
}
}
# --- booking.flight_confirmed (fires after POST .../rerouting/select — Airline Direct) ---
{
"event": "booking.flight_confirmed",
"event_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"created_at": "2026-05-01T09:11:00Z",
"data": {
"disruption_id": "f800450b-20f5-4382-895c-9ecdef8ff5de",
"passenger_id": "NA-PAX-001",
"booking_id": "7a3b9e2c-1d4f-5e6a-8b9c-0d1e2f3a4b5c",
"option_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"carrier": "AA",
"service_number": "AA100",
"departure_at": "2026-05-02T08:00:00Z",
"arrival_at": "2026-05-02T11:30:00Z",
"provider_ref": "X7KM42",
"state": "CONFIRMED"
}
}
# --- case.created (fires when passenger completes claim portal) ---
{
"event": "case.created",
"event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"created_at": "2026-05-01T09:12:34Z",
"data": {
"case_id": "cl_sandbox_001",
"disruption_id": "f800450b-20f5-4382-895c-9ecdef8ff5de",
"passenger_id": "NA-PAX-001",
"pnr": "NK1234",
"flight_number": "NK001",
"flight_date": "2026-05-01",
"disruption_type": "CANCELLED",
"state": "SUBMITTED",
"regulation": "EC261/2004",
"compensation_eligible": true,
"estimated_amount_eur": 600.0,
"portal_url": "https://claim.bluecrestlab.com/nk001-2026-05-01",
"created_at": "2026-05-01T09:12:34Z"
}
}Verifying signatures
Each delivery includes three headers: X-Bluecrest-Signature (hex-encoded HMAC-SHA256 of the raw body, signed with your webhook secret), X-Bluecrest-Event (the event type string, e.g. disruption.ingested), and X-Bluecrest-Delivery (a unique delivery UUID for idempotent processing). Always verify the signature against the raw body bytes before parsing JSON — parsing first can alter whitespace and invalidate the digest.
Bluecrest retries failed deliveries up to 3 times with exponential backoff: 5s → 25s → 125s. A delivery is considered failed if your endpoint returns a non-2xx status or times out after 10s. Sandbox deliveries carry no timestamp or nonce — replay protection is not enforced in sandbox mode.
import hmac, hashlib
def verify_bluecrest_signature(
payload_bytes: bytes,
signature_header: str,
secret: str
) -> bool:
expected = hmac.new(
secret.encode(), payload_bytes, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)
# FastAPI example:
# sig = request.headers.get("X-Bluecrest-Signature")
# body = await request.body()
# if not verify_bluecrest_signature(body, sig, WEBHOOK_SECRET):
# raise HTTPException(status_code=401)Sandbox
POST/v1/dev/sandbox/seed/{scenario_id}wp_sandbox_…) runs against an isolated environment — no real passengers, bookings, or payments. Seed a scenario first, then exercise your integration pattern:Airline Direct — call
POST /v1/partner/airline/disruptions with any PNR and passenger_id values you choose. The response echoes them back — use those same IDs in subsequent compensation and rerouting calls.Sandbox mock data: Rerouting options always return three sandbox flights (AA100, VS4, UA901 — tomorrow UTC) regardless of the route you submit. Compensation eligibility is computed against your actual submitted route and disruption type. Your submitted passenger IDs and PNRs are persisted and echoed back correctly. Production returns live Bluecrest inventory matched to the submitted route.
Kiosk — use the test credentials from the scenario table below.
Webhook deliveries fire against your registered URL in real time. If you are testing locally, expose your endpoint with a tunnel such as ngrok (
ngrok http 3000) before registering a webhook URL — sandbox cannot reach localhost directly.Parameters
scenario_idstring (path)requiredscenario_delay_2h | scenario_cancelled | scenario_claim_eligible# Seed a test scenario curl -X POST https://api.bluecrestlab.com/v1/dev/sandbox/seed/scenario_cancelled \ -H "X-API-Key: YOUR_API_KEY" # Check sandbox state curl https://api.bluecrestlab.com/v1/dev/sandbox/state \ -H "X-API-Key: YOUR_API_KEY" # Reset all sandbox data curl -X POST https://api.bluecrestlab.com/v1/dev/sandbox/reset \ -H "X-API-Key: YOUR_API_KEY"
{
"active_scenarios": ["scenario_cancelled"],
"disruption_events": 1,
"passengers": 5,
"cases": 0,
"last_reset": "2026-05-01T08:00:00Z"
}2-hour delay — BA123
scenario_delay_2hPNR: SBTEST1 / SBTEST2 / SBTEST3 · Last name: SMITH / JONES / PATEL
Tests compensation eligibility (≥2hr delay on EC261 route)
Cancellation — BA456
scenario_cancelledPNR: SBCANC1–5 · Last name: ANDERSON / BROWN / CLARK / DAVIS / EVANS
Tests disruption ingestion and full passenger claim journey
Claim eligible — EK202
scenario_claim_eligiblePNR: SBCLAIM1 · Last name: ANDERSON
Passenger pre-confirmed disrupted — triggers webhooks immediately on ingestion
Errors
All errors return JSON with a detail field describing the problem. Use the HTTP status code to determine the error class.