Integration Guide - Phase 2 Features
Version: 2.0 Last Updated: November 28, 2025
Overview
This guide walks you through integrating the Phase 2 multi-file upload and auto-categorization features into your application. Whether you're building a real estate CRM, buyer portal, or agent tool, this guide provides everything you need.
Quick Start
1. Create a Property
Before uploading documents, create a property to group them:
curl -X POST "https://api.homeinsightai.com/v1/properties" \
-H "Authorization: Bearer hi_live_abc123xyz..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "listing_456",
"address": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip_code": "94102"
}'
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "listing_456",
"address": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip_code": "94102",
"document_count": 0,
"created_at": "2025-11-28T10:00:00Z",
"updated_at": "2025-11-28T10:00:00Z"
}
2. Upload Multiple Documents
Upload all documents for the property in a single batch:
curl -X POST "https://api.homeinsightai.com/v1/properties/550e8400-e29b-41d4-a716-446655440000/upload" \
-H "Authorization: Bearer hi_live_abc123xyz..." \
-F "files=@Home_Inspection_Report.pdf" \
-F "files=@Loan_Estimate.pdf" \
-F "files=@Seller_Disclosure.pdf" \
-F "files=@Purchase_Agreement.pdf"
Response (instant):
{
"property_id": "550e8400-e29b-41d4-a716-446655440000",
"documents_uploaded": 4,
"documents_categorized": 4,
"auto_analyzed": 0,
"queued_for_analysis": 2,
"documents": [
{
"id": "ana_1a2b3c4d5e6f",
"filename": "Home_Inspection_Report.pdf",
"category": "critical",
"is_analyzed": false,
"file_size_bytes": 2458923,
"confidence": 0.95,
"categorization_method": "filename_pattern",
"reasoning": "Filename matches critical patterns"
},
{
"id": "ana_2b3c4d5e6f7g",
"filename": "Loan_Estimate.pdf",
"category": "important",
"is_analyzed": false,
"file_size_bytes": 845672,
"confidence": 0.90,
"categorization_method": "filename_pattern",
"reasoning": "Filename matches important patterns"
},
{
"id": "ana_3c4d5e6f7g8h",
"filename": "Seller_Disclosure.pdf",
"category": "critical",
"is_analyzed": false,
"file_size_bytes": 1234567,
"confidence": 0.95,
"categorization_method": "filename_pattern",
"reasoning": "Filename matches critical patterns"
},
{
"id": "ana_4d5e6f7g8h9i",
"filename": "Purchase_Agreement.pdf",
"category": "optional",
"is_analyzed": false,
"file_size_bytes": 987654,
"confidence": 0.80,
"categorization_method": "filename_pattern",
"reasoning": "Filename matches optional patterns"
}
],
"failed_documents": []
}
Notice:
- Upload completes instantly (2-5 seconds)
- Critical/important documents queued for background analysis
- Optional/noise documents stored as metadata only
3. Chat with Documents
After critical documents complete analysis (~15-30 seconds), you can start asking questions:
curl -X POST "https://api.homeinsightai.com/v1/chat" \
-H "Authorization: Bearer hi_live_abc123xyz..." \
-H "Content-Type: application/json" \
-d '{
"query": "What are the major issues found in the inspection?",
"property_id": "550e8400-e29b-41d4-a716-446655440000"
}'
Response:
{
"answer": "The inspection identified 3 major issues: 1) Roof shingles showing significant wear with curling at edges, estimated 2-3 years remaining life. 2) HVAC system is 18 years old, near end of typical lifespan. 3) Foundation cracks in garage requiring monitoring and potential repair.",
"citations": [
{
"content": "Roof: Composition shingle roof showing wear...",
"page_number": 9,
"source_file": "Home_Inspection_Report.pdf",
"similarity": 0.92
}
],
"context_used": 3,
"confidence": 0.88
}
Complete Workflow Examples
React/TypeScript Integration
import React, { useState } from 'react';
interface Document {
id: string;
filename: string;
category: string;
is_analyzed: boolean;
}
function PropertyDocumentUploader({ propertyId }: { propertyId: string }) {
const [uploading, setUploading] = useState(false);
const [documents, setDocuments] = useState<Document[]>([]);
const handleUpload = async (files: FileList) => {
setUploading(true);
const formData = new FormData();
Array.from(files).forEach(file => {
formData.append('files', file);
});
try {
const response = await fetch(
`https://api.homeinsightai.com/v1/properties/${propertyId}/upload`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.REACT_APP_API_KEY}`,
},
body: formData,
}
);
const result = await response.json();
if (result.failed_documents.length > 0) {
console.error('Some documents failed:', result.failed_documents);
}
setDocuments(result.documents);
// Show success message
alert(`Uploaded ${result.documents_uploaded} documents. ${result.queued_for_analysis} queued for analysis.`);
// Poll for analysis completion
pollAnalysisStatus(propertyId);
} catch (error) {
console.error('Upload failed:', error);
alert('Upload failed. Please try again.');
} finally {
setUploading(false);
}
};
const pollAnalysisStatus = async (propId: string) => {
const checkStatus = async () => {
const response = await fetch(
`https://api.homeinsightai.com/v1/properties/${propId}/documents`,
{
headers: {
'Authorization': `Bearer ${process.env.REACT_APP_API_KEY}`,
},
}
);
const data = await response.json();
const allAnalyzed = data.documents.every(
(doc: any) => doc.status === 'completed' || doc.category === 'optional' || doc.category === 'noise'
);
if (allAnalyzed) {
alert('All critical documents analyzed! Ready to chat.');
return true;
}
return false;
};
// Poll every 5 seconds for up to 2 minutes
const maxAttempts = 24;
for (let i = 0; i < maxAttempts; i++) {
await new Promise(resolve => setTimeout(resolve, 5000));
const done = await checkStatus();
if (done) break;
}
};
return (
<div className="uploader">
<h2>Upload Property Documents</h2>
<input
type="file"
multiple
accept=".pdf"
onChange={(e) => e.target.files && handleUpload(e.target.files)}
disabled={uploading}
/>
{uploading && <p>Uploading and categorizing documents...</p>}
{documents.length > 0 && (
<div className="document-list">
<h3>Uploaded Documents</h3>
<ul>
{documents.map(doc => (
<li key={doc.id}>
<strong>{doc.filename}</strong>
<span className={`badge badge-${doc.category}`}>
{doc.category}
</span>
{doc.is_analyzed ? (
<span className="status-analyzed">✓ Analyzed</span>
) : (
<span className="status-pending">⏳ Processing...</span>
)}
</li>
))}
</ul>
</div>
)}
</div>
);
}
export default PropertyDocumentUploader;
Python Integration
import requests
import time
from typing import List
from pathlib import Path
class HomeInsightClient:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.homeinsightai.com/v1"
self.headers = {"Authorization": f"Bearer {api_key}"}
def create_property(self, external_id: str, address: str, **kwargs) -> dict:
"""Create a new property"""
response = requests.post(
f"{self.base_url}/properties",
headers=self.headers,
json={
"external_id": external_id,
"address": address,
**kwargs
}
)
response.raise_for_status()
return response.json()
def upload_documents(self, property_id: str, file_paths: List[str]) -> dict:
"""Upload multiple documents to a property"""
files = [
("files", (Path(fp).name, open(fp, "rb"), "application/pdf"))
for fp in file_paths
]
response = requests.post(
f"{self.base_url}/properties/{property_id}/upload",
headers=self.headers,
files=files
)
response.raise_for_status()
return response.json()
def get_property_documents(self, property_id: str) -> dict:
"""Get all documents for a property"""
response = requests.get(
f"{self.base_url}/properties/{property_id}/documents",
headers=self.headers
)
response.raise_for_status()
return response.json()
def wait_for_analysis(self, property_id: str, timeout: int = 120) -> bool:
"""Wait for all critical documents to complete analysis"""
start_time = time.time()
while time.time() - start_time < timeout:
docs = self.get_property_documents(property_id)
# Check if all critical/important docs are analyzed
critical_docs = [
d for d in docs["documents"]
if d.get("category") in ("critical", "important")
]
if not critical_docs:
return True
analyzed = [
d for d in critical_docs
if d.get("status") == "completed"
]
if len(analyzed) == len(critical_docs):
print(f"All {len(critical_docs)} critical documents analyzed!")
return True
print(f"Analysis progress: {len(analyzed)}/{len(critical_docs)} documents")
time.sleep(5)
raise TimeoutError(f"Analysis not completed within {timeout} seconds")
def chat(self, property_id: str, query: str) -> dict:
"""Ask a question about property documents"""
response = requests.post(
f"{self.base_url}/chat",
headers=self.headers,
json={
"query": query,
"property_id": property_id
}
)
response.raise_for_status()
return response.json()
# Example usage
if __name__ == "__main__":
client = HomeInsightClient(api_key="hi_live_abc123xyz...")
# 1. Create property
property = client.create_property(
external_id="listing_789",
address="456 Oak Ave",
city="Los Angeles",
state="CA",
zip_code="90001"
)
property_id = property["id"]
print(f"Created property: {property_id}")
# 2. Upload documents
file_paths = [
"documents/Home_Inspection_Report.pdf",
"documents/Loan_Estimate.pdf",
"documents/Seller_Disclosure.pdf",
"documents/Purchase_Agreement.pdf",
]
result = client.upload_documents(property_id, file_paths)
print(f"Uploaded {result['documents_uploaded']} documents")
print(f"Queued for analysis: {result['queued_for_analysis']}")
# 3. Wait for analysis
print("Waiting for critical documents to be analyzed...")
client.wait_for_analysis(property_id)
# 4. Chat with documents
answer = client.chat(
property_id,
"What are the major issues with the roof?"
)
print(f"\nAnswer: {answer['answer']}\n")
print(f"Citations: {len(answer['citations'])} sources")
Node.js/Express Integration
const express = require('express');
const multer = require('multer');
const FormData = require('form-data');
const axios = require('axios');
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
const API_KEY = process.env.HOME_INSIGHT_API_KEY;
const BASE_URL = 'https://api.homeinsightai.com/v1';
// Create property
app.post('/api/properties', async (req, res) => {
try {
const response = await axios.post(
`${BASE_URL}/properties`,
req.body,
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
}
);
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: error.response?.data || error.message,
});
}
});
// Upload documents
app.post('/api/properties/:propertyId/upload', upload.array('files'), async (req, res) => {
try {
const { propertyId } = req.params;
const files = req.files;
if (!files || files.length === 0) {
return res.status(400).json({ error: 'No files provided' });
}
// Create FormData
const formData = new FormData();
files.forEach(file => {
formData.append('files', file.buffer, {
filename: file.originalname,
contentType: 'application/pdf',
});
});
// Upload to Home Insight AI
const response = await axios.post(
`${BASE_URL}/properties/${propertyId}/upload`,
formData,
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
...formData.getHeaders(),
},
}
);
res.json(response.data);
} catch (error) {
console.error('Upload error:', error.response?.data || error.message);
res.status(error.response?.status || 500).json({
error: error.response?.data || error.message,
});
}
});
// Get property documents
app.get('/api/properties/:propertyId/documents', async (req, res) => {
try {
const { propertyId } = req.params;
const response = await axios.get(
`${BASE_URL}/properties/${propertyId}/documents`,
{
headers: { 'Authorization': `Bearer ${API_KEY}` },
}
);
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: error.response?.data || error.message,
});
}
});
// Chat endpoint
app.post('/api/chat', async (req, res) => {
try {
const response = await axios.post(
`${BASE_URL}/chat`,
req.body,
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
}
);
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: error.response?.data || error.message,
});
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Advanced Use Cases
1. Pre-Upload from External Storage
If you already have documents in cloud storage (S3, Google Cloud Storage, etc.), you can pass URLs instead of uploading files:
curl -X POST "https://api.homeinsightai.com/v1/properties/{id}/documents" \
-H "Authorization: Bearer hi_live_abc123xyz..." \
-H "Content-Type: application/json" \
-d '{
"analysis_id": "ana_existing_123"
}'
Workflow:
- Upload documents to your storage (S3, etc.)
- Create analysis via single-file endpoint with pre-signed URL
- Add analysis to property
- Chat with all property documents
2. Bulk Property Import
Import multiple properties with documents:
def bulk_import_properties(client, properties_data):
"""Import properties with documents in bulk"""
results = []
for prop_data in properties_data:
# Create property
property = client.create_property(
external_id=prop_data["external_id"],
address=prop_data["address"],
city=prop_data["city"],
state=prop_data["state"],
zip_code=prop_data["zip_code"]
)
# Upload documents
if prop_data.get("documents"):
upload_result = client.upload_documents(
property["id"],
prop_data["documents"]
)
results.append({
"property_id": property["id"],
"address": property["address"],
"documents_uploaded": upload_result["documents_uploaded"],
"queued_for_analysis": upload_result["queued_for_analysis"]
})
return results
# Example
properties = [
{
"external_id": "listing_001",
"address": "123 Main St",
"city": "SF",
"state": "CA",
"zip_code": "94102",
"documents": ["docs/listing_001_inspection.pdf", "docs/listing_001_loan.pdf"]
},
# ... more properties
]
results = bulk_import_properties(client, properties)
print(f"Imported {len(results)} properties")
3. Categorization Override
If auto-categorization gets it wrong, you can update the category:
curl -X PATCH "https://api.homeinsightai.com/v1/analyses/{analysis_id}" \
-H "Authorization: Bearer hi_live_abc123xyz..." \
-H "Content-Type: application/json" \
-d '{
"category": "critical",
"categorization_metadata": {
"method": "manual_override",
"reason": "User reclassified as critical"
}
}'
Note: Changing category doesn't automatically trigger analysis. You'll need to manually trigger analysis for newly-critical documents.
4. Progress Tracking with WebSockets
For real-time analysis progress updates:
const ws = new WebSocket('wss://api.homeinsightai.com/ws');
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'subscribe',
property_id: '550e8400-e29b-41d4-a716-446655440000',
api_key: 'hi_live_abc123xyz...'
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'analysis_progress') {
console.log(`Analysis ${data.analysis_id}: ${data.progress}% complete`);
updateProgressBar(data.analysis_id, data.progress);
}
if (data.type === 'analysis_complete') {
console.log(`Analysis ${data.analysis_id} complete!`);
enableChatButton();
}
};
Note: WebSocket support is planned for a future release.
Error Handling
Handle Partial Upload Failures
const handleUpload = async (files: File[]) => {
const result = await uploadDocuments(propertyId, files);
// Check for failures
if (result.failed_documents.length > 0) {
console.error('Failed documents:', result.failed_documents);
// Show user which documents failed
result.failed_documents.forEach(failed => {
showError(`${failed.filename}: ${failed.error}`);
});
// Optionally retry failed uploads
const failedFiles = files.filter(f =>
result.failed_documents.some(fd => fd.filename === f.name)
);
if (confirm('Retry failed uploads?')) {
await handleUpload(failedFiles);
}
}
// Process successful uploads
if (result.documents.length > 0) {
showSuccess(`${result.documents.length} documents uploaded successfully`);
}
};
Handle Analysis Timeout
try:
client.wait_for_analysis(property_id, timeout=120)
except TimeoutError:
print("Analysis is taking longer than expected.")
print("You can still chat, but responses may be limited.")
# Continue anyway
answer = client.chat(property_id, "What's available so far?")
print(answer["answer"])
Retry on Rate Limit
async function uploadWithRetry(propertyId, files, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await uploadDocuments(propertyId, files);
} catch (error) {
if (error.response?.status === 429 && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
console.log(`Rate limited. Retrying in ${delay}ms...`);
await sleep(delay);
} else {
throw error;
}
}
}
}
Testing
Test Data
Use these sample documents for testing:
# Download sample documents
curl -O https://homeinsightai.com/samples/sample_inspection_report.pdf
curl -O https://homeinsightai.com/samples/sample_loan_estimate.pdf
curl -O https://homeinsightai.com/samples/sample_disclosure.pdf
Test Categorization
# Test with different filename patterns
test_files=(
"Home_Inspection_Report_Test.pdf"
"Loan_Estimate_Test.pdf"
"Purchase_Agreement_Test.pdf"
"Receipt_Test.pdf"
"Generic_Document.pdf"
)
for file in "${test_files[@]}"; do
echo "Testing: $file"
curl -X POST "http://localhost:8000/v1/properties/{id}/upload" \
-H "Authorization: Bearer hi_test_dev_key_12345" \
-F "files=@$file" \
| jq '.documents[] | {filename, category, confidence}'
done
Migration from Single-File Upload
Before (Single-File)
// Upload one file at a time
for (const file of files) {
await uploadDocument(file); // Sequential, slow
}
After (Multi-File)
// Upload all files at once
await uploadDocuments(propertyId, files); // Parallel, fast
Migration Checklist:
- [ ] Create property before uploading documents
- [ ] Update upload endpoint from
/v1/analysesto/v1/properties/{id}/upload - [ ] Handle array of documents in response
- [ ] Handle
failed_documentsarray - [ ] Check
is_analyzedstatus before chatting - [ ] Update UI to show categorization badges
- [ ] Test with 5-10 files
Best Practices
- Use descriptive filenames:
Home_Inspection_Report_123_Main_St.pdfinstead ofscan.pdf - Batch uploads: Upload all documents at once instead of one-by-one
- Handle failures gracefully: Check
failed_documentsarray and retry - Poll for status: Wait for critical documents to complete analysis
- Show progress: Display categorization and analysis status to users
- Compress PDFs: Reduce file sizes before upload for faster processing
Support
- Documentation: https://docs.homeinsightai.com
- API Reference: https://docs.homeinsightai.com/api
- Discord: https://discord.gg/homeinsightai
- Email: support@homeinsightai.com