CKG Ingestion Asynchronous Notifications
Webhook endpoint for receiving a notification when a FHIR push (CKG data ingestion) job reaches a final state
{
"code": 400,
"message": "invalid request"
}{
"code": 401,
"message": "invalid token"
}{
"code": 500,
"message": "internal server error"
}state of COMPLETED, FAILED, or ABORTED, not a status, and additional_info.session_type is always CKG_INGESTION. Match the notification to your upload with the transaction_id returned from Request FHIR bundle upload URL. The payload has no _links.Code examples
- Python
- TypeScript
import hashlib
import hmac
import json
from flask import Flask, jsonify, request
app = Flask(__name__)
secret_key = "<partner_secret_key>"
@app.route("/webhooks/notification", methods=["POST"])
def handle_webhook():
raw_body = request.get_data(as_text=True)
timestamp = request.headers.get("generated-at", "")
expected_signature_hex = request.headers.get("X-API-Key", "")
computed_signature_hex = hmac.new(
secret_key.encode("utf-8"),
f"{timestamp}:{raw_body}".encode("utf-8"),
hashlib.sha256,
).hexdigest()
if not expected_signature_hex or not hmac.compare_digest(
computed_signature_hex, expected_signature_hex
):
return jsonify({"error": "Invalid signature"}), 401
try:
data = json.loads(raw_body)
except json.JSONDecodeError:
return jsonify({"error": "Invalid request"}), 400
state = data.get("state")
if state in ("COMPLETED", "FAILED", "ABORTED"):
print(f"CKG ingestion {data.get('transaction_id')} {state}")
error = data.get("error") or {}
if error:
print(
f" Error: {error.get('code')} - {error.get('message')} "
f"(retryable: {error.get('retryable')})"
)
return jsonify({"message": "CKG notification received"}), 200
return jsonify({"message": "Ignored"}), 200
if __name__ == "__main__":
app.run(port=3000)
import crypto from "crypto";
import express from "express";
const app = express();
const secretKey = "<partner_secret_key>";
app.post("/webhooks/notification", express.raw({ type: "application/json" }), (req, res) => {
const rawBody = Buffer.isBuffer(req.body) ? req.body.toString("utf8") : String(req.body ?? "");
const timestamp = String(req.headers["generated-at"] ?? "");
const expectedSignatureHex = String(req.headers["x-api-key"] ?? "");
const computedSignatureHex = crypto
.createHmac("sha256", secretKey)
.update(`${timestamp}:${rawBody}`, "utf8")
.digest("hex");
const signaturesMatch =
Boolean(expectedSignatureHex) &&
computedSignatureHex.length === expectedSignatureHex.length &&
crypto.timingSafeEqual(
Buffer.from(computedSignatureHex, "utf8"),
Buffer.from(expectedSignatureHex, "utf8"),
);
if (!signaturesMatch) {
return res.status(401).json({ error: "Invalid signature" });
}
let data: Record<string, any>;
try {
data = JSON.parse(rawBody);
} catch {
return res.status(400).json({ error: "Invalid request" });
}
const state = data.state;
if (state === "COMPLETED" || state === "FAILED" || state === "ABORTED") {
console.log(`CKG ingestion ${data.transaction_id} ${state}`);
if (data.error) {
console.log(
` Error: ${data.error.code} - ${data.error.message} (retryable: ${data.error.retryable})`,
);
}
return res.status(200).json({ message: "CKG notification received" });
}
return res.status(200).json({ message: "Ignored" });
});
app.listen(3000, () => {
console.log("Webhook server listening on port 3000");
});
Body
CKG ingestion notification payload.
Notification for a CKG Data Push. additional_info.session_type is CKG_INGESTION.
Transaction identifier returned when you requested the upload URL.
"46b88f6d-..."
Partner-facing organization identifier, not the internal Suki organization id.
"your-org-id"
Correlation identifier for the upload. Suki generates one when you do not send it.
"2e3a1724-..."
State of the CKG Data Push. Aborted uploads are reported as ABORTED.
COMPLETED, FAILED, ABORTED "COMPLETED"
When the ingestion job was created, in UTC. Omitted when unavailable.
"2026-07-09T10:12:43Z"
When the ingestion job reached its final state, in UTC. Omitted when unavailable.
"2026-07-09T10:13:11Z"
Present on FAILED and ABORTED payloads when an error was recorded.
Show child attributes
Show child attributes
Partner-level context. session_type is CKG_INGESTION.
Show child attributes
Show child attributes
Response
OK
Was this page helpful?
{
"code": 400,
"message": "invalid request"
}{
"code": 401,
"message": "invalid token"
}{
"code": 500,
"message": "internal server error"
}