Form Filling Notifications
Form Filling Asynchronous Notifications
Webhook endpoint for receiving asynchronous notifications when Form filling session processing completes
Listen to this pageabout 1 min
POST
/
webhooks
/
notification
Receive asynchronous notifications (partner webhook)
curl --request POST \
--url https://sdp.suki.ai/webhooks/notification \
--header 'Content-Type: application/json' \
--data '
{
"encounter_id": "29de56bc-960a-4cd5-b18f-79a798d62874",
"error_code": "ERROR_CODE_TRANSCRIPTION",
"error_detail": "Error in transcription",
"session_id": "20965414-929a-4f71-a3e5-b92bec07d086",
"status": "FAILURE"
}
'import requests
url = "https://sdp.suki.ai/webhooks/notification"
payload = {
"encounter_id": "29de56bc-960a-4cd5-b18f-79a798d62874",
"error_code": "ERROR_CODE_TRANSCRIPTION",
"error_detail": "Error in transcription",
"session_id": "20965414-929a-4f71-a3e5-b92bec07d086",
"status": "FAILURE"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
encounter_id: '29de56bc-960a-4cd5-b18f-79a798d62874',
error_code: 'ERROR_CODE_TRANSCRIPTION',
error_detail: 'Error in transcription',
session_id: '20965414-929a-4f71-a3e5-b92bec07d086',
status: 'FAILURE'
})
};
fetch('https://sdp.suki.ai/webhooks/notification', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sdp.suki.ai/webhooks/notification",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'encounter_id' => '29de56bc-960a-4cd5-b18f-79a798d62874',
'error_code' => 'ERROR_CODE_TRANSCRIPTION',
'error_detail' => 'Error in transcription',
'session_id' => '20965414-929a-4f71-a3e5-b92bec07d086',
'status' => 'FAILURE'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sdp.suki.ai/webhooks/notification"
payload := strings.NewReader("{\n \"encounter_id\": \"29de56bc-960a-4cd5-b18f-79a798d62874\",\n \"error_code\": \"ERROR_CODE_TRANSCRIPTION\",\n \"error_detail\": \"Error in transcription\",\n \"session_id\": \"20965414-929a-4f71-a3e5-b92bec07d086\",\n \"status\": \"FAILURE\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sdp.suki.ai/webhooks/notification")
.header("Content-Type", "application/json")
.body("{\n \"encounter_id\": \"29de56bc-960a-4cd5-b18f-79a798d62874\",\n \"error_code\": \"ERROR_CODE_TRANSCRIPTION\",\n \"error_detail\": \"Error in transcription\",\n \"session_id\": \"20965414-929a-4f71-a3e5-b92bec07d086\",\n \"status\": \"FAILURE\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sdp.suki.ai/webhooks/notification")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"encounter_id\": \"29de56bc-960a-4cd5-b18f-79a798d62874\",\n \"error_code\": \"ERROR_CODE_TRANSCRIPTION\",\n \"error_detail\": \"Error in transcription\",\n \"session_id\": \"20965414-929a-4f71-a3e5-b92bec07d086\",\n \"status\": \"FAILURE\"\n}"
response = http.request(request)
puts response.read_bodyThis response has no body data.{
"code": 400,
"message": "invalid request"
}{
"code": 401,
"message": "invalid token"
}{
"code": 500,
"message": "internal server error"
}Use this endpoint to implement an HTTP endpoint in your application that receives requests from Suki.
This endpoint should be hosted by your application to receive notifications about Form filling completion or failure.
Learn more about how webhooks work and how to implement your webhook endpoint in Notification webhook for partners.
Host this endpoint on your server. Suki sends a POST when Form filling processing completes or fails. The payload
session_id is the Form filling session ID from Create Form filling session.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
status = data.get("status")
if status == "SUCCESS":
session_id = data.get("session_id")
encounter_id = data.get("encounter_id")
print(f"Form filling session {session_id} completed (encounter {encounter_id})")
links = data.get("_links") or {}
for link in links.get("structured_data") or []:
print(f" Structured data: {link.get('method')} {link.get('href')}")
for link in links.get("status") or []:
print(f" Status: {link.get('method')} {link.get('href')}")
return jsonify({"message": "Notification received"}), 200
if status == "FAILURE":
print(
f"Form filling session {data.get('session_id')} failed "
f"(encounter {data.get('encounter_id')}): "
f"{data.get('error_code')} - {data.get('error_detail')}"
)
return jsonify({"message": "Failure notification received"}), 200
return jsonify({"error": "Unknown status"}), 400
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" });
}
if (data.status === "SUCCESS") {
console.log(
`Form filling session ${data.session_id} completed (encounter ${data.encounter_id})`,
);
const links = data._links ?? {};
(links.structured_data ?? []).forEach((link: { method?: string; href?: string }) => {
console.log(` Structured data: ${link.method} ${link.href}`);
});
(links.status ?? []).forEach((link: { method?: string; href?: string }) => {
console.log(` Status: ${link.method} ${link.href}`);
});
return res.status(200).json({ message: "Notification received" });
}
if (data.status === "FAILURE") {
console.log(
`Form filling session ${data.session_id} failed (encounter ${data.encounter_id}): ` +
`${data.error_code} - ${data.error_detail}`,
);
return res.status(200).json({ message: "Failure notification received" });
}
return res.status(400).json({ error: "Unknown status" });
});
app.listen(3000, () => {
console.log("Webhook server listening on port 3000");
});
Body
application/json
Webhook payload Suki sends when session processing fails.
Id of the encounter to which the payload belongs.
Example:
"29de56bc-960a-4cd5-b18f-79a798d62874"
Error code.
Example:
"ERROR_CODE_TRANSCRIPTION"
Details of the error, if any.
Example:
"Error in transcription"
Id of the session that failed.
Example:
"20965414-929a-4f71-a3e5-b92bec07d086"
Example:
"FAILURE"
Response
OK
Last modified on October 16, 2026
Was this page helpful?
Receive asynchronous notifications (partner webhook)
curl --request POST \
--url https://sdp.suki.ai/webhooks/notification \
--header 'Content-Type: application/json' \
--data '
{
"encounter_id": "29de56bc-960a-4cd5-b18f-79a798d62874",
"error_code": "ERROR_CODE_TRANSCRIPTION",
"error_detail": "Error in transcription",
"session_id": "20965414-929a-4f71-a3e5-b92bec07d086",
"status": "FAILURE"
}
'import requests
url = "https://sdp.suki.ai/webhooks/notification"
payload = {
"encounter_id": "29de56bc-960a-4cd5-b18f-79a798d62874",
"error_code": "ERROR_CODE_TRANSCRIPTION",
"error_detail": "Error in transcription",
"session_id": "20965414-929a-4f71-a3e5-b92bec07d086",
"status": "FAILURE"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
encounter_id: '29de56bc-960a-4cd5-b18f-79a798d62874',
error_code: 'ERROR_CODE_TRANSCRIPTION',
error_detail: 'Error in transcription',
session_id: '20965414-929a-4f71-a3e5-b92bec07d086',
status: 'FAILURE'
})
};
fetch('https://sdp.suki.ai/webhooks/notification', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sdp.suki.ai/webhooks/notification",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'encounter_id' => '29de56bc-960a-4cd5-b18f-79a798d62874',
'error_code' => 'ERROR_CODE_TRANSCRIPTION',
'error_detail' => 'Error in transcription',
'session_id' => '20965414-929a-4f71-a3e5-b92bec07d086',
'status' => 'FAILURE'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sdp.suki.ai/webhooks/notification"
payload := strings.NewReader("{\n \"encounter_id\": \"29de56bc-960a-4cd5-b18f-79a798d62874\",\n \"error_code\": \"ERROR_CODE_TRANSCRIPTION\",\n \"error_detail\": \"Error in transcription\",\n \"session_id\": \"20965414-929a-4f71-a3e5-b92bec07d086\",\n \"status\": \"FAILURE\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sdp.suki.ai/webhooks/notification")
.header("Content-Type", "application/json")
.body("{\n \"encounter_id\": \"29de56bc-960a-4cd5-b18f-79a798d62874\",\n \"error_code\": \"ERROR_CODE_TRANSCRIPTION\",\n \"error_detail\": \"Error in transcription\",\n \"session_id\": \"20965414-929a-4f71-a3e5-b92bec07d086\",\n \"status\": \"FAILURE\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sdp.suki.ai/webhooks/notification")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"encounter_id\": \"29de56bc-960a-4cd5-b18f-79a798d62874\",\n \"error_code\": \"ERROR_CODE_TRANSCRIPTION\",\n \"error_detail\": \"Error in transcription\",\n \"session_id\": \"20965414-929a-4f71-a3e5-b92bec07d086\",\n \"status\": \"FAILURE\"\n}"
response = http.request(request)
puts response.read_bodyThis response has no body data.{
"code": 400,
"message": "invalid request"
}{
"code": 401,
"message": "invalid token"
}{
"code": 500,
"message": "internal server error"
}