AnchorRun your first BI dashboard locally (Metabase)
This tutorial helps you launch a local BI stack (Metabase + Postgres) and start
exploring data right away. We’ll use the open-source demo project
immoteur/webhooks-ingest-demo,
which includes a small webhook ingestion API and a Metabase setup that reads
directly from the database.
If you’d like to explore the result before running anything locally, open the live demo.
Tip: Use
make demofor live mode (real webhook traffic viasmee.io). If you don’t have a source yet,make demo-with-seedinjects sample events so your dashboards are not empty.
AnchorWhat you'll build
- A local Metabase instance you can open in your browser.
- A Postgres database populated with webhook events and analytics-friendly tables.
- A first dashboard you can inspect, remix, and share locally.
AnchorPrerequisites
- Docker + Docker Compose (v2)
- Node.js 20+ (required by the demo scripts)
- Git + Make
- Free ports on your machine (defaults:
8080,3001,15432)
AnchorQuick links
- Demo repository: https://github.com/immoteur/webhooks-ingest-demo
- Metabase documentation: https://www.metabase.com/docs/latest/
- smee.io: https://smee.io/
AnchorArchitecture diagram
AnchorStep-by-step guide
Anchor1) Clone the demo project
git clone https://github.com/immoteur/webhooks-ingest-demo.git;
cd webhooks-ingest-demo;Anchor2) Start the stack (recommended)
Use live mode (real data) with the full Docker Compose stack + smee.io relays:
make demo;Example output:
[demo] Waiting for API at http://localhost:8080...
[+] Running 1/1
✔ Container webhooks_ingest_api Healthy 17.6s
[demo] Ready (live mode, no seed):
- API: http://localhost:8080
- Metabase: http://localhost:3001
- Metabase admin: [email protected] / DemoAdmin!2025ChangeMe
- Metabase public dashboard: http://localhost:3001/public/dashboard/16b57a07-9831-4ec9-b927-b6207d3eaa31
[demo] smee relays (send webhooks here):
- classified-notification: https://smee.io/classified-notification-3o41g78qykci2otd
- classifieds-export: https://smee.io/classifieds-export-holes6hi6y85rx31What this does for you:
- creates
.envfrom.env.exampleif needed - starts Postgres + the ingestion API + Metabase
- bootstraps Metabase (admin user + DB connection)
- generates and starts 2
smee.iorelays (one per webhook endpoint) - prints URLs and credentials (including a public Metabase dashboard URL)
Metabase admin credentials also come from .env (defaults in the demo):
[email protected]METABASE_ADMIN_PASSWORD=DemoAdmin!2025ChangeMe
Anchor3) Open Metabase and verify the dashboard
- Open
http://localhost:3001 - Log in with
METABASE_ADMIN_EMAIL/METABASE_ADMIN_PASSWORD - Open the demo dashboard (or the public dashboard URL printed by
make demo)

.env.
Anchor4) Explore the data model (your first BI dataset)
In Metabase, you should see tables like:
webhook_events: raw ingestion log of received webhook requests (with retention cleanup)classifieds: flattened records derived from Immoteur payloads (classified-notification/classifieds-export)classified_images: one row per image (joined toclassifieds)classified_price_history: one row per price change (joined toclassifieds)
Try a first question in Metabase’s SQL editor:
select count(*) as events from webhook_events;Anchor5) Send real events (or post sample payloads)
After make demo, your terminal prints 2 public relay URLs:
[demo] smee relays (send webhooks here):
- classified-notification: https://smee.io/...
- classifieds-export: https://smee.io/...classifieds-export payloads can be large; if your relay rejects them (for example
with HTTP 413), send them directly to the local API (see the cURL tab) or use
make demo-with-seed.
Configure your Immoteur webhooks to send events to these smee.io
URLs (one per endpoint). When an event is emitted, it’s relayed to your local
ingestion API and written to Postgres.

Create an exporter in Immoteur (open
https://immoteur.com/dashboard/services#create)
and paste the classified-notification relay URL.
Back in Metabase:
- re-run your question or refresh the dashboard
- check
webhook_eventscount increased
Anchor6) Stop (or reset) the demo
To stop containers (keep data):
make stack-down-smee;To wipe everything (fresh start, deletes Docker volumes):
make stack-reset-smee;AnchorConclusion
You now have a local BI stack running: an ingestion API that writes to Postgres, and Metabase reading from it to power dashboards. Next, try creating your own question (e.g., “events per hour”), add it to a dashboard, and point the webhook endpoints to a real source when you’re ready.