Example: Handling HITL Task Callbacks
Learn how to build a listener endpoint to asynchronously receive and process notifications for Human-in-the-Loop (HITL) task events from KirokuForms.
Asynchronous HITL with Callbacks
When your AI system creates a Human-in-the-Loop task using the KirokuForms MCP API or Python SDK, you can provide a callbackUrl
. When the status of this task changes (e.g., a human reviewer completes it), KirokuForms will send an HTTP POST request (a callback, which is a type of webhook) to your specified URL.
This event-driven approach is highly efficient as it avoids the need for your application to continuously poll KirokuForms for task status updates, especially useful for tasks that might take a significant amount of time for human review.
This page focuses on a specific example for HITL task callbacks. For a comprehensive understanding of general webhook principles, managing form-level webhooks (e.g., for all new submissions to a specific form), detailed signature verification steps, and troubleshooting, please refer to our main Webhooks Documentation.
1. Setting the Callback URL
You specify your listener's URL in the callbackUrl
parameter when creating a HITL task:
Using the Python SDK:
# When creating a HITL task with the Python SDK:
hitl_task = kiroku_hitl_client.create_task(
title="Review Document Classification",
# ... other task parameters (fields, etc.) ...
callback_url="https://your-server.com/api/kirokuforms-hitl-callback" # Your listener endpoint
)
Using the MCP API Directly:
# When creating a HITL task directly via MCP API:
# POST https://www.kirokuforms.com/api/mcp/tools/request-human-review
# Payload:
{
"taskTitle": "Verify Extracted Entities",
"inputData": { "text_to_review": "Some text..." },
"fields": [
{ "name": "is_correct", "type": "radio", "label": "Correct?", "options": ["Yes", "No"] },
{ "name": "comments", "type": "textarea", "label": "Comments" }
],
"callbackUrl": "https://your-server.com/api/kirokuforms-hitl-callback"
}
Ensure https://your-server.com/api/kirokuforms-hitl-callback
is replaced with your actual, publicly accessible endpoint URL.
2. Expected HITL Callback Payload
When a subscribed HITL task event occurs (e.g., hitl.task.completed
), KirokuForms sends a JSON payload to your callbackUrl
. Here's an example structure:
{
"eventType": "hitl.task.completed", // Could also be hitl.task.updated, etc.
"taskId": "task_abc123xyz789", // The KirokuForms Task ID
"timestamp": "2025-07-15T11:45:00Z",
"data": {
"status": "completed",
"formData": {
// Key-value pairs of the data submitted by the human reviewer
"is_correct": "Yes",
"comments": "The entities look accurate."
}
// Potentially other task-related metadata
}
}
Your listener should parse this payload to extract the eventType
, taskId
, and the submitted formData
within the data
object.
3. Example: Simple HITL Callback Listener (Python/Flask)
Below is a basic example of a Flask application in Python that can receive and process these HITL task callbacks. Remember to implement robust signature verification in a production environment as detailed in our main Webhooks Documentation.
from flask import Flask, request, jsonify
import hmac
import hashlib
import os
import json
app = Flask(__name__)
# Store your secret securely, e.g., in an environment variable
# This secret should match what KirokuForms uses to sign this specific callback/webhook
KIROKU_HITL_CALLBACK_SECRET = os.environ.get("KIROKU_HITL_SECRET")
def verify_signature(payload_body, signature_header):
if not KIROKU_HITL_CALLBACK_SECRET:
print("Webhook secret not configured. Skipping signature verification (NOT FOR PRODUCTION!).")
return True # Insecure, for local testing only if secret is not set
if not signature_header:
print("Signature header missing.")
return False
# Assuming KirokuForms-Signature format: t=timestamp,v1=signature
# This is an example; adapt to KirokuForms' actual HITL callback signature scheme.
try:
timestamp_str, sig_v1_str = signature_header.split(',')
timestamp = timestamp_str.split('=')[1]
provided_signature = sig_v1_str.split('=')[1]
# Optional: Check timestamp staleness (e.g., within 5 minutes)
# ...
string_to_sign = f"{timestamp}.{payload_body}" # Note: payload_body must be raw string
expected_signature = hmac.new(
KIROKU_HITL_CALLBACK_SECRET.encode('utf-8'),
string_to_sign.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected_signature, provided_signature)
except Exception as e:
print(f"Error verifying signature: {e}")
return False
@app.route('/api/kirokuforms-hitl-callback', methods=['POST'])
def kiroku_hitl_callback():
raw_body = request.get_data(as_text=True) # Get raw body for signature verification
signature = request.headers.get('KirokuForms-Signature') # Or actual header name
# ** ALWAYS VERIFY THE SIGNATURE IN PRODUCTION **
# if not verify_signature(raw_body, signature):
# print("Webhook signature verification failed!")
# return jsonify({"error": "Invalid signature"}), 400
try:
payload = json.loads(raw_body) # Now parse JSON
except json.JSONDecodeError:
print("Invalid JSON payload received.")
return jsonify({"error": "Invalid JSON"}), 400
print(f"Received HITL Callback - Event Type: {payload.get('eventType')}, Task ID: {payload.get('taskId')}")
if payload.get('eventType') == 'hitl.task.completed':
task_id = payload.get('taskId')
form_data = payload.get('data', {}).get('formData', {})
print(f"HITL Task {task_id} completed. Data: {form_data}")
# Add your processing logic here:
# - Update your database
# - Resume a paused workflow (e.g., a LangGraph agent)
# - Send further notifications, etc.
# Acknowledge receipt quickly
return jsonify({"status": "success", "message": "Callback received"}), 200
if __name__ == '__main__':
# For local testing, you might use ngrok to expose this endpoint
# app.run(port=5001, debug=True)
pass
The signature verification part in the example above is conceptual and commented out. **Always implement and enforce signature verification for all incoming webhooks in your production environment.** This ensures the data originates from KirokuForms and hasn't been tampered with. Refer to the full Webhooks Security Guide for detailed instructions.
4. Expanding Your HITL Workflow
Once your listener receives a callback for a completed HITL task, you can:
- Store the human-provided
formData
in your database. - Use the feedback to fine-tune your AI models.
- If using LangGraph with checkpoints, this callback might trigger an external process to resume the graph execution with the human input.
- Notify relevant stakeholders or kick off subsequent business processes.
5. Other Notification & Integration Options
Beyond programmatic HITL task callbacks and general form webhooks, KirokuForms offers other ways to stay informed:
- Email Notifications for Form Submissions: For standard forms, you can easily configure email notifications to be sent to specified addresses upon new submissions. This is typically set up directly in the form's settings via the KirokuForms web UI dashboard.
- Upcoming Integrations: We are always working to expand our integration capabilities. Native integrations with popular services (e.g., Slack, Zapier, CRMs) are on our roadmap.
Have a specific integration or notification type you need? We'd love to hear about it! Request an Integration or share your feedback.
Further Reading
- Deep dive into the Python SDK for HITL.
- Understand the core HITL API functionalities via MCP.
- See more complex scenarios in our main HITL Examples collection.
- Review general best practices in the main Webhooks Documentation.