Rather than polling, you can register a webhook endpoint to be notified when
a review reaches a terminal state.
Events
| Event | Fired when |
|---|
review.completed | The review finished and its report is available. |
review.failed | The review could not be completed. |
Payload
{
"event": "review.completed",
"review_id": "9b1f....",
"reference_id": "LN-2026-0042",
"status": "completed",
"occurred_at": "2026-05-30T18:09:12Z"
}
The payload intentionally carries only identifiers and status; fetch
GET /reviews/{review_id}/report to retrieve the result over an
authenticated channel.
Verifying signatures
Each delivery is signed so you can verify it came from Flightline. The
signature is sent in the X-Flightline-Signature header as
t=<timestamp>,v1=<hex-hmac>, where the HMAC is HMAC_SHA256(secret, "<t>.<raw-body>")
using the signing secret shown when you register the endpoint.
import hashlib, hmac
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
signed = f"{parts['t']}.{raw_body.decode()}".encode()
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
Reject any delivery whose signature does not verify, or whose timestamp is
older than a few minutes.
Managing endpoints
Register, list, and delete your webhook endpoints with your organization API
key (Authorization: Bearer <key>), the same key you use for every other
/v1 call.
| Method | Path | Purpose |
|---|
| POST | /v1/webhook-endpoints | Register an endpoint; returns the signing secret once. |
| GET | /v1/webhook-endpoints | List your registered endpoints. |
| DELETE | /v1/webhook-endpoints/{id} | Delete an endpoint. |
Register
curl -X POST https://api.flightlinehq.com/v1/webhook-endpoints \
-H "Authorization: Bearer $FLIGHTLINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/hooks/flightline", "description": "prod"}'
{
"id": "5f0c....",
"url": "https://example.com/hooks/flightline",
"enabled": true,
"description": "prod",
"secret_hint": "whsec_…a1b2",
"created_at": "2026-05-30T18:09:12Z",
"signing_secret": "whsec_3f9c...store-this-now"
}
The signing_secret is returned only in this registration response. Store
it immediately; it cannot be retrieved again. List and delete responses
return only a non-reversible secret_hint, never the secret itself.
The endpoint URL must be HTTPS and resolve to a public host. URLs pointing
at internal, private, loopback, link-local, or cloud-metadata addresses are
rejected with a 400.
List
curl https://api.flightlinehq.com/v1/webhook-endpoints \
-H "Authorization: Bearer $FLIGHTLINE_API_KEY"
{
"endpoints": [
{
"id": "5f0c....",
"url": "https://example.com/hooks/flightline",
"enabled": true,
"description": "prod",
"secret_hint": "whsec_…a1b2",
"created_at": "2026-05-30T18:09:12Z"
}
]
}
Delete
curl -X DELETE https://api.flightlinehq.com/v1/webhook-endpoints/5f0c... \
-H "Authorization: Bearer $FLIGHTLINE_API_KEY"
Returns 204 No Content. Endpoints are scoped to your organization; an id
that belongs to another organization returns 404.
Delivery, retries, and security
- Transport:
POST with a JSON body to your registered https URL.
Endpoints must be https; internal/private hosts are rejected at
registration.
- Retries: up to 3 attempts with exponential backoff (~0.5s, 1s) on a
non-2xx response or transport error. Return a
2xx quickly (within ~10s) to
acknowledge; we time out a slow endpoint and retry.
- Best-effort: webhooks are a convenience, not the source of truth. If
every attempt fails, the review state is unchanged; always treat
GET /reviews/{review_id} / …/report as authoritative, and reconcile on a
schedule as a backstop.
- Verify the signature (see above) on every delivery. Reject any delivery
whose signature doesn’t verify, or whose
t timestamp is more than 5
minutes old, to prevent replay.
- Idempotency: a delivery may arrive more than once. De-duplicate on
(
review_id, event); processing should be idempotent.
- Source: deliveries originate from Flightline’s infrastructure without a
fixed source-IP range; authenticate them by verifying the signature,
not by IP allowlisting.