Webhooks API Documentation
Version: 2.0 Last Updated: December 6, 2025 Status: Production Ready
Overview
Webhooks allow you to receive real-time notifications during document processing. Home Insight AI uses a Two-Phase Processing architecture that delivers value faster by notifying you as soon as each document's AI analysis completes (~60 seconds per document), rather than waiting for the entire batch.
Key Benefits
- Real-Time Updates: Get notified within ~60 seconds per document
- Two-Phase Architecture: First webhook when AI analysis completes, second when chat is ready
- Reduce API Calls: No need to poll for status
- Secure Delivery: All webhooks are signed with HMAC-SHA256
- Retry Logic: Failed deliveries are automatically retried
- Audit Trail: Complete delivery history for debugging
Webhook Events
| Event | Phase | Description |
|---|---|---|
document.analysis_completed | Phase 1 | AI analysis complete for a single document (~60s). You can now fetch issues and summary. |
document.skipped | Phase 1 | Document was skipped (uncategorized or noise). Use force-analyze endpoint to process. |
document.embedding_failed | Phase 2 | Embedding generation failed after all retries. Chat may be limited for this document. |
property.chat_ready | Phase 2 | All documents embedded and indexed. RAG chat is now fully operational. |
webhook.test | - | Test event sent from the dashboard or API. |
Two-Phase Processing Flow
- Phase 1 (Analysis): Each document is processed individually. You receive
document.analysis_completedordocument.skippedwithin ~60 seconds per document. - Phase 2 (Embeddings): After all documents complete Phase 1, embeddings are generated in batch. You receive
property.chat_readywhen complete.
Note: The document.analysis_completed webhook includes the analysis_id so you can immediately fetch results without waiting for the entire batch.
Managing Webhooks
Register a Webhook
POST /v1/webhooks
Create a new webhook endpoint.
curl -X POST "https://api.homeinsightai.com/v1/webhooks" \
-H "Authorization: Bearer hi_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/homeinsight",
"events": ["document.analysis_completed", "property.chat_ready"],
"description": "Production webhook"
}'
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS URL to receive webhooks (must be HTTPS in production) |
events | array | Yes | List of event types to subscribe to |
description | string | No | Optional description for your reference |
active | boolean | No | Enable/disable webhook (default: true) |
Response (201 Created):
{
"id": "a6691d07-ca77-4f7e-8d75-8dde5277017d",
"url": "https://your-app.com/webhooks/homeinsight",
"events": ["document.analysis_completed", "property.chat_ready"],
"active": true,
"description": "Production webhook",
"signing_secret": "whsec_abc123xyz...",
"created_at": "2025-12-03T10:30:00Z",
"updated_at": "2025-12-03T10:30:00Z"
}
Important: The signing_secret is only returned once when creating the webhook. Store it securely - you'll need it to verify webhook signatures.
List Webhooks
GET /v1/webhooks
Retrieve all your registered webhooks.
curl "https://api.homeinsightai.com/v1/webhooks" \
-H "Authorization: Bearer hi_live_abc123..."
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
limit | integer | Max webhooks to return (default: 20, max: 100) |
offset | integer | Pagination offset (default: 0) |
active | boolean | Filter by active status |
Response:
{
"webhooks": [
{
"id": "a6691d07-ca77-4f7e-8d75-8dde5277017d",
"url": "https://your-app.com/webhooks/homeinsight",
"events": ["document.analysis_completed", "property.chat_ready"],
"active": true,
"description": "Production webhook",
"created_at": "2025-12-03T10:30:00Z",
"updated_at": "2025-12-03T10:30:00Z"
}
],
"total": 1,
"limit": 20,
"offset": 0
}
Get Webhook Details
GET /v1/webhooks/{webhook_id}
Get details for a specific webhook including delivery statistics.
curl "https://api.homeinsightai.com/v1/webhooks/a6691d07-ca77-4f7e-8d75-8dde5277017d" \
-H "Authorization: Bearer hi_live_abc123..."
Response:
{
"id": "a6691d07-ca77-4f7e-8d75-8dde5277017d",
"url": "https://your-app.com/webhooks/homeinsight",
"events": ["document.analysis_completed", "property.chat_ready"],
"active": true,
"description": "Production webhook",
"created_at": "2025-12-03T10:30:00Z",
"updated_at": "2025-12-03T10:30:00Z",
"delivery_stats": {
"total_deliveries": 150,
"successful_deliveries": 145,
"failed_deliveries": 5,
"success_rate": 96.7
}
}
Update Webhook
PATCH /v1/webhooks/{webhook_id}
Update webhook configuration.
curl -X PATCH "https://api.homeinsightai.com/v1/webhooks/a6691d07-ca77-4f7e-8d75-8dde5277017d" \
-H "Authorization: Bearer hi_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"events": ["document.analysis_completed", "document.skipped", "property.chat_ready"],
"active": true
}'
Note: The signing secret cannot be changed. Delete and recreate the webhook if you need a new secret.
Delete Webhook
DELETE /v1/webhooks/{webhook_id}
Delete a webhook. All delivery history will also be deleted.
curl -X DELETE "https://api.homeinsightai.com/v1/webhooks/a6691d07-ca77-4f7e-8d75-8dde5277017d" \
-H "Authorization: Bearer hi_live_abc123..."
Response: 204 No Content
Test Webhook
POST /v1/webhooks/{webhook_id}/test
Send a test event to verify your webhook configuration.
curl -X POST "https://api.homeinsightai.com/v1/webhooks/a6691d07-ca77-4f7e-8d75-8dde5277017d/test" \
-H "Authorization: Bearer hi_live_abc123..."
Response:
{
"delivery_id": "88155eed-30c5-413f-ba75-12589044c769",
"message": "Test webhook sent",
"status": "delivered"
}
Webhook Payload Format
All webhooks are sent as HTTP POST requests with a JSON body.
Headers
| Header | Description |
|---|---|
Content-Type | application/json |
X-HomeInsight-Signature | HMAC-SHA256 signature for verification (format: sha256=BASE64_SIGNATURE) |
X-HomeInsight-Timestamp | Unix timestamp when event was sent |
X-HomeInsight-Event | Event type (e.g., document.analysis_completed) |
X-HomeInsight-Delivery-ID | Unique delivery ID for debugging |
User-Agent | HomeInsight-Webhook/1.0 |
Example: document.analysis_completed (Phase 1)
This is the primary webhook event for real-time updates. Sent within ~60 seconds of upload for each document.
{
"event": "document.analysis_completed",
"analysis_id": "ana_1a2b3c4d5e6f",
"property_id": "550e8400-e29b-41d4-a716-446655440000",
"job_id": "job_abc123xyz",
"filename": "inspection_report.pdf",
"document_type": "inspection_report",
"status": "analysis_done",
"page_count": 47,
"total_issues": 12,
"severity": "major",
"completed_at": "2025-12-04T10:35:00.123456"
}
Payload Fields:
| Field | Type | Description |
|---|---|---|
event | string | Event type (document.analysis_completed) |
analysis_id | string | UUID of the document analysis. Use this to fetch results immediately. |
property_id | string | UUID of the property |
job_id | string | UUID of the processing job (from upload response) |
filename | string | Original filename of the uploaded document |
document_type | string | Auto-detected type: inspection_report, disclosure, loan_estimate, etc. |
status | string | analysis_done - ready for summary/issues retrieval |
page_count | integer | Number of pages in the document |
total_issues | integer | Number of issues found (for inspection reports) |
severity | string | Overall severity: critical, major, minor |
completed_at | string | ISO 8601 timestamp when analysis finished |
Example: document.skipped (Phase 1)
Sent when a document is skipped because it was categorized as uncategorized or noise.
{
"event": "document.skipped",
"analysis_id": "ana_2b3c4d5e6f7g",
"property_id": "550e8400-e29b-41d4-a716-446655440000",
"job_id": "job_abc123xyz",
"filename": "receipt.pdf",
"document_type": "uncategorized",
"status": "skipped",
"reason": "Document categorized as uncategorized - not a critical home purchase document",
"skipped_at": "2025-12-04T10:35:05.123456"
}
Note: You can force-analyze skipped documents using POST /v1/analyses/{analysis_id}/analyze.
Example: property.chat_ready (Phase 2)
Sent when all documents have been embedded and RAG chat is fully operational.
{
"event": "property.chat_ready",
"property_id": "550e8400-e29b-41d4-a716-446655440000",
"job_id": "job_abc123xyz",
"total_documents": 4,
"analyzed_documents": 3,
"skipped_documents": 1,
"embedded_documents": 3,
"status": "chat_ready",
"completed_at": "2025-12-04T10:38:00.123456"
}
Example: document.embedding_failed (Phase 2)
Sent when embedding generation fails for a document after all retry attempts.
{
"event": "document.embedding_failed",
"analysis_id": "ana_3c4d5e6f7g8h",
"property_id": "550e8400-e29b-41d4-a716-446655440000",
"job_id": "job_abc123xyz",
"filename": "disclosure.pdf",
"error": "Embedding service timeout after 3 retries",
"failed_at": "2025-12-04T10:37:30.123456"
}
Note: Documents with failed embeddings will have limited or no chat functionality, but their analysis results are still available.
Example: webhook.test
{
"event": "webhook.test",
"message": "Test webhook delivery",
"timestamp": "2025-12-04T10:35:00.123456"
}
Recommended Workflow: Two-Phase Processing
The Two-Phase Processing architecture delivers value faster by notifying you as soon as each document's analysis completes.
Step 1: Upload Documents
curl -X POST "https://api.homeinsightai.com/v1/properties/{property_id}/upload" \
-H "Authorization: Bearer hi_live_abc123..." \
-F "files=@inspection_report.pdf" \
-F "files=@seller_disclosure.pdf"
Response:
{
"property_id": "prop_abc123",
"job_id": "job_xyz789",
"documents": [
{
"id": "ana_1a2b3c4d5e6f",
"filename": "inspection_report.pdf",
"status": "queued",
"page_count": 47,
"category": "critical"
},
{
"id": "ana_2b3c4d5e6f7g",
"filename": "seller_disclosure.pdf",
"status": "queued",
"page_count": 12,
"category": "critical"
}
],
"total_documents": 2,
"status": "processing"
}
Step 2: Receive Phase 1 Webhooks (Per-Document)
Within ~60 seconds, you'll receive a document.analysis_completed webhook for each document:
{
"event": "document.analysis_completed",
"analysis_id": "ana_1a2b3c4d5e6f",
"property_id": "prop_abc123",
"job_id": "job_xyz789",
"filename": "inspection_report.pdf",
"document_type": "inspection_report",
"status": "analysis_done",
"page_count": 47,
"total_issues": 12,
"severity": "major",
"completed_at": "2025-12-04T10:35:00.123456"
}
Immediately fetch results - don't wait for all documents:
# Get analysis summary
curl "https://api.homeinsightai.com/v1/analyses/ana_1a2b3c4d5e6f/summary" \
-H "Authorization: Bearer hi_live_abc123..."
# Get structured issues with cost estimates
curl "https://api.homeinsightai.com/v1/analyses/ana_1a2b3c4d5e6f/issues" \
-H "Authorization: Bearer hi_live_abc123..."
Step 3: Handle Skipped Documents (Optional)
If a document is categorized as uncategorized or noise, you'll receive:
{
"event": "document.skipped",
"analysis_id": "ana_3c4d5e6f7g8h",
"filename": "receipt.pdf",
"document_type": "uncategorized",
"reason": "Document categorized as uncategorized - not a critical home purchase document"
}
To force-analyze a skipped document:
curl -X POST "https://api.homeinsightai.com/v1/analyses/ana_3c4d5e6f7g8h/analyze" \
-H "Authorization: Bearer hi_live_abc123..."
Step 4: Receive Phase 2 Webhook (Chat Ready)
After all documents complete Phase 1 and embeddings are generated:
{
"event": "property.chat_ready",
"property_id": "prop_abc123",
"job_id": "job_xyz789",
"total_documents": 2,
"analyzed_documents": 2,
"embedded_documents": 2,
"status": "chat_ready",
"completed_at": "2025-12-04T10:38:00.123456"
}
Now RAG chat is fully operational for this property.
Alternative: Fetch All Property Documents
To get all documents for a property (across all upload sessions):
curl "https://api.homeinsightai.com/v1/properties/prop_abc123/documents" \
-H "Authorization: Bearer hi_live_abc123..."
Signature Verification
All webhooks are signed using HMAC-SHA256. Always verify signatures to ensure webhooks are from Home Insight AI.
Verification Steps
- Get the signature from the
X-HomeInsight-Signatureheader - Get the timestamp from the
X-HomeInsight-Timestampheader - Construct the signed payload:
{timestamp}.{raw_body} - Compute HMAC-SHA256 using your signing secret
- Compare with the provided signature
Python Example
import hmac
import hashlib
import base64
def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
"""Verify webhook signature"""
# Construct signed payload
message = f"{timestamp}.{payload.decode('utf-8')}"
# Compute expected signature
expected = hmac.new(
secret.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).digest()
expected_b64 = base64.b64encode(expected).decode('utf-8')
# Compare signatures
return hmac.compare_digest(f"sha256={expected_b64}", signature)
# In your webhook handler
from flask import Flask, request
app = Flask(__name__)
WEBHOOK_SECRET = "whsec_abc123xyz..."
@app.route("/webhooks/homeinsight", methods=["POST"])
def handle_webhook():
signature = request.headers.get("X-HomeInsight-Signature")
timestamp = request.headers.get("X-HomeInsight-Timestamp")
if not verify_webhook(request.data, signature, timestamp, WEBHOOK_SECRET):
return "Invalid signature", 401
event = request.json
if event["event"] == "property.completed":
# Handle completion
property_id = event["data"]["property_id"]
print(f"Property {property_id} processing complete!")
return "OK", 200
Node.js Example
const crypto = require('crypto');
const express = require('express');
const app = express();
const WEBHOOK_SECRET = 'whsec_abc123xyz...';
function verifyWebhook(payload, signature, timestamp, secret) {
const message = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(message)
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(`sha256=${expected}`),
Buffer.from(signature)
);
}
app.post('/webhooks/homeinsight', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-homeinsight-signature'];
const timestamp = req.headers['x-homeinsight-timestamp'];
if (!verifyWebhook(req.body.toString(), signature, timestamp, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
if (event.event === 'property.completed') {
console.log(`Property ${event.data.property_id} processing complete!`);
}
res.status(200).send('OK');
});
Retry Policy
Failed webhook deliveries are automatically retried with exponential backoff:
| Attempt | Delay | Cumulative Time |
|---|---|---|
| 1 (initial) | Immediate | 0s |
| 2 | 30 seconds | 30s |
| 3 | 2 minutes | 2m 30s |
| 4 | 10 minutes | 12m 30s |
| 5 (final) | 30 minutes | 42m 30s |
After 5 failed attempts, the delivery is marked as permanently failed and moved to a dead letter queue.
Success Criteria
A delivery is considered successful when your endpoint returns:
- HTTP status code 2xx (200-299)
- Response within 30 seconds
Failure Criteria
A delivery is marked as failed when:
- HTTP status code 4xx or 5xx
- Connection timeout (30 seconds)
- DNS resolution failure
- SSL certificate error
Best Practices
1. Respond Quickly
Return a 200 response immediately, then process the webhook asynchronously:
@app.route("/webhooks/homeinsight", methods=["POST"])
def handle_webhook():
# Verify signature
if not verify_webhook(request.data, ...):
return "Invalid signature", 401
# Queue for async processing
webhook_queue.enqueue(process_webhook, request.json)
# Return immediately
return "OK", 200
2. Handle Duplicates
Webhooks may be delivered more than once. Use the X-HomeInsight-Delivery-ID header for idempotency:
def process_webhook(event, delivery_id):
if redis.exists(f"webhook:{delivery_id}"):
return # Already processed
# Process event
handle_event(event)
# Mark as processed (expire after 24 hours)
redis.setex(f"webhook:{delivery_id}", 86400, "processed")
3. Verify Signatures
Always verify webhook signatures in production. Never trust unverified webhooks.
4. Use HTTPS
Always use HTTPS endpoints in production. HTTP endpoints will be rejected.
5. Monitor Delivery Stats
Check your webhook delivery statistics regularly in the dashboard to identify issues early.
Rate Limits
| Plan | Max Webhooks | Events/Minute |
|---|---|---|
| Free Trial | 2 | 10 |
| Pay-Per-Report | 5 | 30 |
| Pro | 10 | 100 |
| Enterprise | Unlimited | Unlimited |
Troubleshooting
Webhook Not Received
- Check webhook is active: Verify in dashboard that webhook is enabled
- Check event subscription: Ensure you're subscribed to the correct events
- Check firewall: Ensure your server accepts connections from Home Insight AI IPs
- Check SSL: Verify your SSL certificate is valid and not expired
Signature Verification Failing
- Use raw body: Don't parse JSON before verifying signature
- Check secret: Ensure you're using the correct signing secret
- Check timestamp: Use the exact timestamp from the header
- Check encoding: Ensure UTF-8 encoding throughout
High Failure Rate
- Check response time: Ensure your endpoint responds within 30 seconds
- Check response code: Return 2xx status codes for success
- Check server logs: Look for errors in your webhook handler
- Scale infrastructure: Add more capacity if under load
Support
- Documentation: https://docs.homeinsightai.com
- API Status: https://status.homeinsightai.com
- Email: support@homeinsightai.com