KLIQ|Developers

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 2500

NEN 2767 Condition Grades

GradeConditionDescription
1ExcellentNew or as-new condition
2GoodMinor wear, no action needed
3ModerateVisible wear, plan maintenance
4PoorSignificant degradation, schedule repair
5Very PoorFailed or critical, immediate action
6End of LifeBeyond 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