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

EventPhaseDescription
document.analysis_completedPhase 1AI analysis complete for a single document (~60s). You can now fetch issues and summary.
document.skippedPhase 1Document was skipped (uncategorized or noise). Use force-analyze endpoint to process.
document.embedding_failedPhase 2Embedding generation failed after all retries. Chat may be limited for this document.
property.chat_readyPhase 2All documents embedded and indexed. RAG chat is now fully operational.
webhook.test-Test event sent from the dashboard or API.

Two-Phase Processing Flow

  1. Phase 1 (Analysis): Each document is processed individually. You receive document.analysis_completed or document.skipped within ~60 seconds per document.
  2. Phase 2 (Embeddings): After all documents complete Phase 1, embeddings are generated in batch. You receive property.chat_ready when 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.

Shell
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:

FieldTypeRequiredDescription
urlstringYesHTTPS URL to receive webhooks (must be HTTPS in production)
eventsarrayYesList of event types to subscribe to
descriptionstringNoOptional description for your reference
activebooleanNoEnable/disable webhook (default: true)

Response (201 Created):

JSON
{
  "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.

Shell
curl "https://api.homeinsightai.com/v1/webhooks" \
  -H "Authorization: Bearer hi_live_abc123..."

Query Parameters:

ParameterTypeDescription
limitintegerMax webhooks to return (default: 20, max: 100)
offsetintegerPagination offset (default: 0)
activebooleanFilter by active status

Response:

JSON
{
  "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.

Shell
curl "https://api.homeinsightai.com/v1/webhooks/a6691d07-ca77-4f7e-8d75-8dde5277017d" \
  -H "Authorization: Bearer hi_live_abc123..."

Response:

JSON
{
  "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.

Shell
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.

Shell
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.

Shell
curl -X POST "https://api.homeinsightai.com/v1/webhooks/a6691d07-ca77-4f7e-8d75-8dde5277017d/test" \
  -H "Authorization: Bearer hi_live_abc123..."

Response:

JSON
{
  "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

HeaderDescription
Content-Typeapplication/json
X-HomeInsight-SignatureHMAC-SHA256 signature for verification (format: sha256=BASE64_SIGNATURE)
X-HomeInsight-TimestampUnix timestamp when event was sent
X-HomeInsight-EventEvent type (e.g., document.analysis_completed)
X-HomeInsight-Delivery-IDUnique delivery ID for debugging
User-AgentHomeInsight-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.

JSON
{
  "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:

FieldTypeDescription
eventstringEvent type (document.analysis_completed)
analysis_idstringUUID of the document analysis. Use this to fetch results immediately.
property_idstringUUID of the property
job_idstringUUID of the processing job (from upload response)
filenamestringOriginal filename of the uploaded document
document_typestringAuto-detected type: inspection_report, disclosure, loan_estimate, etc.
statusstringanalysis_done - ready for summary/issues retrieval
page_countintegerNumber of pages in the document
total_issuesintegerNumber of issues found (for inspection reports)
severitystringOverall severity: critical, major, minor
completed_atstringISO 8601 timestamp when analysis finished

Example: document.skipped (Phase 1)

Sent when a document is skipped because it was categorized as uncategorized or noise.

JSON
{
  "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.

JSON
{
  "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.

JSON
{
  "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

JSON
{
  "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

Shell
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:

JSON
{
  "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:

JSON
{
  "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:

Shell
# 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:

JSON
{
  "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:

Shell
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:

JSON
{
  "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):

Shell
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

  1. Get the signature from the X-HomeInsight-Signature header
  2. Get the timestamp from the X-HomeInsight-Timestamp header
  3. Construct the signed payload: {timestamp}.{raw_body}
  4. Compute HMAC-SHA256 using your signing secret
  5. Compare with the provided signature

Python Example

Python
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

JavaScript
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:

AttemptDelayCumulative Time
1 (initial)Immediate0s
230 seconds30s
32 minutes2m 30s
410 minutes12m 30s
5 (final)30 minutes42m 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:

Python
@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:

Python
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

PlanMax WebhooksEvents/Minute
Free Trial210
Pay-Per-Report530
Pro10100
EnterpriseUnlimitedUnlimited

Troubleshooting

Webhook Not Received

  1. Check webhook is active: Verify in dashboard that webhook is enabled
  2. Check event subscription: Ensure you're subscribed to the correct events
  3. Check firewall: Ensure your server accepts connections from Home Insight AI IPs
  4. Check SSL: Verify your SSL certificate is valid and not expired

Signature Verification Failing

  1. Use raw body: Don't parse JSON before verifying signature
  2. Check secret: Ensure you're using the correct signing secret
  3. Check timestamp: Use the exact timestamp from the header
  4. Check encoding: Ensure UTF-8 encoding throughout

High Failure Rate

  1. Check response time: Ensure your endpoint responds within 30 seconds
  2. Check response code: Return 2xx status codes for success
  3. Check server logs: Look for errors in your webhook handler
  4. Scale infrastructure: Add more capacity if under load

Support

  • Documentation: https://docs.homeinsightai.com
  • API Status: https://status.homeinsightai.com
  • Email: support@homeinsightai.com
Home Insight AI - Developer Portal