Building Inspection (BuildingScan)
This recipe walks through a complete BuildingScan integration: from registering buildings and assets to running multi-stage CV pipelines and generating inspection reports.
What you will build
An automated building inspection pipeline that identifies building components, grades their condition using NEN 2767 standards, and generates planned preventive maintenance (PPM) budgets.
1. Set up your tenant
import { KliqClient } from '@kliq-ai/sdk';
const kliq = new KliqClient({
apiKey: process.env.KLIQ_API_KEY!,
});
const tenant = kliq.forTenant('your-tenant-id');2. Add buildings
const building = await tenant.buildings.create({
name: 'Office Tower A',
address: '100 Business Park, Amsterdam',
buildYear: 1998,
totalArea: 12500, // m2
floors: 8,
metadata: {
type: 'office',
portfolio: 'Amsterdam West',
manager: 'Jan de Vries',
},
});
console.log('Building ID:', building.id);3. Register assets
Assets are the individual components to inspect (HVAC units, fire systems, elevators, facades, etc.):
const assets = await Promise.all([
tenant.assets.create({
buildingId: building.id,
name: 'HVAC Unit - Rooftop',
category: 'HVAC',
floor: 'roof',
installDate: '2015-06-01',
metadata: { brand: 'Carrier', model: '38AUZ' },
}),
tenant.assets.create({
buildingId: building.id,
name: 'Fire Detection Panel',
category: 'Fire Safety',
floor: '1',
installDate: '2020-03-15',
metadata: { brand: 'Siemens', model: 'FC724' },
}),
tenant.assets.create({
buildingId: building.id,
name: 'Elevator #1',
category: 'Vertical Transport',
floor: 'all',
installDate: '1998-01-01',
metadata: { brand: 'Schindler', capacity: '1000kg' },
}),
]);
console.log(`Registered ${assets.length} assets`);4. Submit inspection photos
// Submit multiple photos per asset
const photos = [
'https://bucket.com/hvac-front.jpg',
'https://bucket.com/hvac-filters.jpg',
'https://bucket.com/hvac-nameplate.jpg',
];
const observations = await tenant.observations.createBatch(
photos.map(url => ({
locationId: building.id,
imageUrl: url,
metadata: {
assetId: assets[0].id,
inspectionType: 'annual',
inspector: 'jan.devries',
},
}))
);
console.log(`Submitted ${observations.length} inspection photos`);5. Run multi-stage CV pipeline
BuildingScan uses a router model that detects the asset type, then dispatches to specialist models:
// Start CV jobs for all observation photos
const jobs = await Promise.all(
observations.map(obs =>
tenant.cv.start({
observationId: obs.id,
model: 'building-inspection-v1', // auto-routes to specialist
})
)
);
// Wait for all to complete
const results = await Promise.all(
jobs.map(job => tenant.cv.waitForCompletion(job.id))
);6. Process component inventory and grading
for (const result of results) {
const { detections } = result.result;
for (const component of detections) {
console.log(`Component: ${component.objectName}`);
console.log(` Condition: ${component.attributes.nen2767Grade}`);
console.log(` Defects: ${component.attributes.defects?.join(', ') || 'None'}`);
console.log(` Estimated repair cost: EUR ${component.attributes.estimatedCost}`);
}
}
// Example output:
// Component: Air Handling Unit
// Condition: 3 (Moderate)
// Defects: corrosion, filter_degradation
// Estimated repair cost: EUR 2500NEN 2767 Condition Grades
| Grade | Condition | Description |
|---|---|---|
| 1 | Excellent | New or as-new condition |
| 2 | Good | Minor wear, no action needed |
| 3 | Moderate | Visible wear, plan maintenance |
| 4 | Poor | Significant degradation, schedule repair |
| 5 | Very Poor | Failed or critical, immediate action |
| 6 | End of Life | Beyond repair, replace |
7. Generate PPM budget
// Aggregate costs by category for budget planning
const budget = new Map<string, number>();
for (const result of results) {
for (const detection of result.result.detections) {
const category = detection.attributes.category || 'Other';
const cost = detection.attributes.estimatedCost || 0;
budget.set(category, (budget.get(category) || 0) + cost);
}
}
console.log('PPM Budget Estimate:');
for (const [category, total] of budget) {
console.log(` ${category}: EUR ${total.toLocaleString()}`);
}8. Track inspection history
// Record a visit
const visit = await tenant.visits.create({
buildingId: building.id,
inspectorId: 'user_jan',
observationIds: observations.map(o => o.id),
notes: 'Annual inspection. HVAC filters need replacement.',
scheduledDate: '2026-04-28',
completedDate: '2026-04-28',
});
// Query historical inspections
const history = await tenant.visits.list({
buildingId: building.id,
limit: 10,
});
for (const v of history.data) {
console.log(`${v.completedDate}: ${v.observationIds.length} photos, ${v.notes}`);
}Next steps
- Shelf Monitoring recipe — ShelfSignal integration
- CV Jobs guide — Job lifecycle and models
- Webhooks guide — Real-time notifications