# Belgian Building-Permit Monitor — Omgevingsvergunning Delta (`masterly_function/belgian-building-permit-monitor`) Actor

Delta monitor for the Flemish building-permit register (Omgevingsloket Inzage): new permits, public-openings, decisions — surfaced run-over-run as fresh events, filterable by gemeente and stage. Not a stateless re-export. Source: Vlaanderen open data (Modellicentie voor gratis hergebruik).

- **URL**: https://apify.com/masterly_function/belgian-building-permit-monitor.md
- **Developed by:** [Vesper](https://apify.com/masterly_function) (community)
- **Stats:** 2 total users, 1 monthly users, 93.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Belgian Building-Permit Monitor — Flemish Omgevingsvergunning Delta

Delta monitor for the **Flemish building-permit register** ([Omgevingsloket Inzage](https://omgevingsloketinzage.omgeving.vlaanderen.be)) — new permits surfaced **run-over-run as fresh events**, not a stateless re-export. Each run performs a full-register scan (~10 requests, seconds) and emits only what is **new since the previous run** of the same actor.

### Why a delta monitor

The register is public but passive: it is a rolling window you would have to re-read daily and diff by hand — thousands of rows, rows also silently vanish. This actor does the diff for you: every event it emits is **new since the last run** (or new under your filters), so the output is a clean daily signal.

### Use cases

- **Demolition and renovation contractors** — a fresh permit is the earliest reliable signal of an upcoming project; demolition is typically the first trade on site. Build a pipeline of sites before they exist.
- **Architects & developers** — watch filings appear in your municipalities.
- **Notaries & real-estate** — track permit activity around streets you already cover.
- **Researchers & press** — a structured daily feed of permit flow per municipality, with geographic points for mapping.

### What an event looks like

- `event: APPEARED` — a project just appeared in the register (or under your filters) at this run: project number, name, gemeente, stage, address, Lambert-72 WKT point + WGS84 lat/lon.
- `event: DISAPPEARED` (optional, off by default) — a previously-seen project vanished from the register (it is a rolling window; rows expire).
- `recordType: run_status` — one line per run with scan/delta counters (the actor's own heartbeat).

**Stages:** `AKTENAME` (fresh filing — earliest signal), `OPENBAARONDERZOEK` (public inquiry open), `BESLISSING` (decision recorded). Default emits the two earliest.

### Input

| Field | Type | Meaning |
|---|---|---|
| `gemeenten` | array of strings | Official Flemish municipality names, case-insensitive (e.g. `["Antwerpen", "Waregem"]`). Empty = all of Flanders. |
| `stageAktename` / `stageOpenbaaronderzoek` / `stageBeslissing` | booleans | Which stages emit: fresh filing / public inquiry / decision. First two on, decision off by default (decision is historical and high-volume). |
| `emitDisappeared` | boolean | Also emit tombstone events for vanished rows. Default `false`. |
| `fetchDetails` | boolean | Add the project's top "voorwerpen" (e.g. `kaartLabel` like "company — subject") per emitted event; a few extra requests per event. Default `false`. |

### Scheduling

Run it daily (Flanders sees roughly 30–80 fresh filings state-wide per day). Delta state lives in the actor's own named key-value store — nothing to configure.

**Two things to expect:**

1. **First run seeds silently.** A run whose input shape has never been seen emits no events — it records the register's current state, and events start from your *second* run. This is deliberate: a first run emitting ~9,000 rows is a re-export, not a delta.
2. **The delta cursor is scoped to your input.** Runs with different input (municipalities/stages/flags) each keep their own cursor; runs with *identical* input share one — if you run the exact default input, your cursor is shared with other default-input runs, so the first run of a day sees the most. To get a dedicated cursor with near-default semantics, pass anything that changes the output, e.g. `{"fetchDetails": true}`; a run with a non-default flag *value* (e.g. explicitly `false` where the default is already `false`) resolves to the same filter set and still shares.

### Notes on the data

- **Source:** Flanders — Omgevingsloket Inzage, open data. Re-use under the Flemish "Modellicentie voor gratis hergebruik" (free re-use with attribution).
- **Point coordinates** are Belgian Lambert 72 (EPSG:31370) exactly as the register emits them; WGS84 `lat`/`lon` are derived (Belge 1972→WGS84 transformation, ~metre-class accuracy).
- **No per-project deep-links:** the register offers no verified stable permalink per project, so events carry the register base URL and the `projectnummer` — search it in the Omgevingsloket Inzage.
- **Privacy:** events deliberately carry **no applicant or company names/contact details**. The register's own document chain (one click deeper, freely public) contains them; we leave that decision and its compliance burden to the user of the data, and we exclude names from our output by design.
- Register contents change between a filing and publication — the register is the authority; this monitor's value is *timing and filtering*, not legal status.

### Development / source

- Full source in the actor repository; the register client, PoW handling and delta engine are small, readable Node modules.
- Fixture-driven tests (`npm test`) run entirely against saved register snapshots — no live hits in tests.

### About the developer

**Vesper** — independent developer of small, verified open-data monitors on Belgian public registers. Every parser is fixture-tested; full source ships with the actor.

# Actor input Schema

## `gemeenten` (type: `array`):

Official municipality names of interest (Flanders: e.g. Antwerpen, Waregem). Case-insensitive exact match against the register's behandelende overheid. Empty array = all municipalities.

## `stageAktename` (type: `boolean`):

Register intake — the earliest signal that a permit is in flight. Recommended on.

## `stageOpenbaaronderzoek` (type: `boolean`):

Public inquiry opened — the second signal.

## `stageBeslissing` (type: `boolean`):

Decision recorded (historical, high volume — costs more events). Off by default.

## `emitDisappeared` (type: `boolean`):

Also emit an event when a project that was previously seen has vanished from the register (the register is a rolling window; rows silently expire).

## `fetchDetails` (type: `boolean`):

Add the register's own top-'voorwerpen' for each emitted event (e.g. kaartLabel 'company — subject' lead line). Costs a few extra requests per event; off by default.

## Actor input object example

```json
{
  "gemeenten": [
    "Antwerpen",
    "Waregem"
  ],
  "stageAktename": true,
  "stageOpenbaaronderzoek": true,
  "stageBeslissing": false,
  "emitDisappeared": false,
  "fetchDetails": false
}
```

# Actor output Schema

## `results` (type: `string`):

All rows: permits_event delta events (APPEARED / DISAPPEARED) and run_status lines.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("masterly_function/belgian-building-permit-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("masterly_function/belgian-building-permit-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call masterly_function/belgian-building-permit-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,masterly_function/belgian-building-permit-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/XLZdnFWFG4UG8vYrq/builds/pdQ2pBBZdPtcB9hOB/openapi.json
