AfvalBakkieOntwikkelaars

Voor ontwikkelaars

AfvalBakkie werkt samen met bestaande afvalapps, afvalplatforms en chatbots. Een app blijft gewoon bestaan; Bakkie brengt dezelfde informatie naar inwoners in WhatsApp. Hier staat hoe je koppelt, met voorbeelden die je direct kunt proberen.

KoppelingAdresToegang
MCP-serverPOST https://www.afval-bakkie.nl/mcpOpenbaar, alleen lezen
AgendafeedGET https://www.afval-bakkie.nl/api/ics/{postcode}/{huisnummer}.icsOpenbaar
KoppelcodePOST https://www.afval-bakkie.nl/api/v1/koppelcodeAPI-sleutel per partner
Meldingen per e-mailnaar het e-mailadres dat de gemeente insteltInstelling per gemeente

Testen kan op https://uat.afval-bakkie.nl: dezelfde koppelingen, met testgegevens. Een sleutel voor de testomgeving is een andere dan voor productie.

MCP-server

Met het Model Context Protocol gebruikt elke AI-assistent de kennis van Bakkie: de chatbot op de website van een gemeente, Copilot, ChatGPT of Claude. De server werkt met Streamable HTTP en JSON-RPC 2.0, en zonder sessie: elke aanvraag staat op zichzelf, een initialize vooraf mag maar hoeft niet. Alleen POST; een GET geeft 405.

Toevoegen aan een MCP-client

Configuratie (Claude Desktop, Cursor en andere clients)
{
  "mcpServers": {
    "afvalbakkie": { "url": "https://www.afval-bakkie.nl/mcp" }
  }
}
Claude Code
claude mcp add --transport http afvalbakkie https://www.afval-bakkie.nl/mcp

Tools

ToolWat het doet en de invoer
zoek_adresZoekt een Nederlands adres op in de BAG (via PDOK) en geeft straat, woonplaats en gemeente terug.
  • postcode string: Postcode, bijv. 1404AR
  • huisnummer integer
  • toevoeging string, optioneel
ophaaldagenGeeft de ophaaldagen (afvalkalender) voor een adres: welke container wanneer aan de straat moet. Inclusief gewijzigde dagen rond feestdagen. Gebruik dit voor elke vraag over wanneer afval wordt opgehaald.
  • postcode string, optioneel: Postcode, bijv. 1404AR. Laat leeg in de app: dan wordt het adres van de inwoner gebruikt.
  • huisnummer integer, optioneel: Huisnummer
  • toevoeging string, optioneel: Huisletter of toevoeging
  • vanaf string, optioneel: Startdatum YYYY-MM-DD (standaard vandaag)
  • dagen integer, optioneel: Aantal dagen vooruit (standaard 14). Gebruik 30 als de inwoner om de kalender, een overzicht of alle ophaaldagen vraagt.
  • afvalsoort "gft" | "pmd" | "papier" | "rest" | "textiel", optioneel: Alleen deze afvalsoort
afval_scheidenZoekt in de afvalscheidingswijzer waar een voorwerp of materiaal hoort (GFT, PMD, papier, restafval, glas, textiel, milieustraat, statiegeld, e-waste, klein chemisch afval…), met tips en wat het wordt na recycling. Houdt rekening met regels van de gemeente (bijv. nascheiding).
  • voorwerp string: Het voorwerp of materiaal, zo kort mogelijk, bijv. "pizzadoos" of "lege spuitbus"
  • postcode string, optioneel: Postcode, bijv. 1404AR. Laat leeg in de app: dan wordt het adres van de inwoner gebruikt.
  • huisnummer integer, optioneel: Huisnummer
  • toevoeging string, optioneel: Huisletter of toevoeging
inzamelpuntenVindt inzamelpunten in de buurt van het adres: glasbakken, textielcontainers, papier- en PMD-containers, ondergrondse containers, batterij- en e-waste-inzamelpunten, kringloopwinkels. Bron: containerdata van de gemeente en OpenStreetMap.
  • soort "alle" | "glas" | "textiel" | "papier" | "pmd" | "rest" | "batterijen" | "ewaste" | "frituurvet" | "kringloop" | "milieustraat": Soort inzamelpunt
  • postcode string, optioneel: Postcode, bijv. 1404AR. Laat leeg in de app: dan wordt het adres van de inwoner gebruikt.
  • huisnummer integer, optioneel: Huisnummer
  • toevoeging string, optioneel: Huisletter of toevoeging
milieustraat_infoGeeft de dichtstbijzijnde milieustraat (afvalbrengstation) met openingstijden, of hij nu open is, verwachte drukte per uur en welke afvalsoorten er worden geaccepteerd.
  • postcode string, optioneel: Postcode, bijv. 1404AR. Laat leeg in de app: dan wordt het adres van de inwoner gebruikt.
  • huisnummer integer, optioneel: Huisnummer
  • toevoeging string, optioneel: Huisletter of toevoeging
melding_makenMaakt een melding bij de gemeente: container niet geleegd, container kapot of kwijt, zwerfafval, afvaldumping/bijplaatsing, ondergrondse container vol. In de app wordt eerst een concept getoond dat de inwoner zelf bevestigt.
  • type "niet_geleegd" | "container_kapot" | "container_weg" | "zwerfafval" | "dumping" | "ondergrondse_vol" | "overig"
  • omschrijving string: Korte omschrijving in de woorden van de inwoner
  • afvalsoort "gft" | "pmd" | "papier" | "rest" | "textiel" | "glas", optioneel: Welke container het betreft, indien van toepassing
  • locatie string, optioneel: Locatie als die afwijkt van het huisadres (bijv. "hoek Kerkstraat/Molenweg")
  • postcode string, optioneel: Postcode, bijv. 1404AR. Laat leeg in de app: dan wordt het adres van de inwoner gebruikt.
  • huisnummer integer, optioneel: Huisnummer
  • toevoeging string, optioneel: Huisletter of toevoeging
weer_en_afvalWeersverwachting (Open-Meteo) voor de komende dagen met afvaladvies voor ophaaldagen: harde wind, hitte (maden in GFT), vorst.
  • postcode string, optioneel: Postcode, bijv. 1404AR. Laat leeg in de app: dan wordt het adres van de inwoner gebruikt.
  • huisnummer integer, optioneel: Huisnummer
  • toevoeging string, optioneel: Huisletter of toevoeging
kennisbankTips over afval scheiden, GFT-onderhoud, voedselverspilling en duurzaamheid, en het ontkrachten van afvalmythes.
  • onderwerp "alles" | "mythe" | "gft" | "verspilling" | "duurzaam" | "tip", optioneel
nieuwsLaatste nieuws en berichten over afval in de gemeente.geen invoer
barcode_opzoekenZoekt een product op via de barcode (EAN) in Open Food Facts en leidt uit het verpakkingsmateriaal af in welke bak de onderdelen horen.
  • barcode string: EAN-barcode (8–13 cijfers)

Adresgebonden tools vragen postcode en huisnummer. melding_maken bereidt een melding voor; de inwoner rondt die af in WhatsApp.

Aanroepen met curl

Stuur Accept: application/json, text/event-stream mee. Het antwoord komt als één server-sent event; het resultaat staat in content[0].text als JSON.

Aanvraag
curl -X POST https://www.afval-bakkie.nl/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"afval_scheiden","arguments":{"voorwerp":"pizzadoos"}}}'
Antwoord
event: message
data: {"result":{"content":[{"type":"text","text":"{\n  \"gevonden\": true,\n  \"resultaten\": [ { \"voorwerp\": \"Pizzadoos\", \"hoort_bij\": \"Papier & karton\", \"waar\": \"Blauwe container\", \"tip\": \"Vetvlekken zijn geen probleem. Etensresten eruit (GFT).\" } ] }"}],"isError":false},"jsonrpc":"2.0","id":1}
Inhoud van content[0].text bij ophaaldagen (ingekort)
{
  "adres": "Kerkstraat 12, 1234AB Voorbeelddorp",
  "vandaag": "maandag 28 september",
  "ophaaldagen": [
    { "datum": "2026-09-28", "dag": "vandaag (maandag 28 september)", "afval": "Papier & karton", "container": "Blauwe container" },
    { "datum": "2026-10-01", "dag": "donderdag 1 oktober", "afval": "GFT", "container": "Groene container" }
  ]
}

Met een MCP-bibliotheek

JavaScript / TypeScript (@modelcontextprotocol/sdk)
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const client = new Client({ name: 'mijn-app', version: '1.0.0' });
await client.connect(new StreamableHTTPClientTransport(new URL('https://www.afval-bakkie.nl/mcp')));

const res = await client.callTool({
  name: 'ophaaldagen',
  arguments: { postcode: '1234AB', huisnummer: 12 },
});
const data = JSON.parse(res.content[0].text);
console.log(data.ophaaldagen[0]); // { datum, dag, afval, container }
await client.close();
Python (mcp 2.x)
import asyncio, json
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client

async def main():
    async with streamable_http_client("https://www.afval-bakkie.nl/mcp") as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("afval_scheiden", {"voorwerp": "pizzadoos"})
            print(json.loads(result.content[0].text)["resultaten"][0]["hoort_bij"])

asyncio.run(main())

Fouten: een tool die niets kan vinden geeft gewoon een resultaat met uitleg; een ongeldige aanroep geeft isError: true of een JSON-RPC-fout. Te veel aanvragen geeft HTTP 429.

Agendafeed (ICS)

De ophaaldagen per adres als agenda-abonnement: twee weken terug en een jaar vooruit. Agenda's halen de feed dagelijks opnieuw op, dus wijzigingen komen vanzelf mee. Elke ophaaldag heeft een herinnering om 19:00 uur de avond ervoor.

GET https://www.afval-bakkie.nl/api/ics/{postcode}/{huisnummer}.ics
DeelUitleg
{postcode}Zonder spatie, bijvoorbeeld 1234AB
{huisnummer}Met toevoeging na een streepje: 12, 12-A, 12-2HG
?streams=Optioneel, alleen deze afvalsoorten: gft, pmd, papier, rest, textiel (komma's ertussen)
Antwoord200 text/calendar, of 404 als het adres niet bestaat of er (nog) geen ophaaldagen zijn
Voorbeeld (ingekort)
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//AfvalBakkie//NL
X-WR-CALNAME:Afvalkalender Kerkstraat 12
REFRESH-INTERVAL;VALUE=DURATION:P1D
BEGIN:VEVENT
UID:1234AB-12-gft-2026-10-01@afvalbakkie
DTSTART;VALUE=DATE:20261001
DTEND;VALUE=DATE:20261002
SUMMARY:GFT ophalen
DESCRIPTION:Groene container. Los in de container\, zonder plastic zakje.
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Morgen GFT: zet de container vanavond buiten
TRIGGER:-PT5H
END:VALARM
END:VEVENT
END:VCALENDAR
Knop in een app of op een website
<a href="webcal://www.afval-bakkie.nl/api/ics/1234AB/12.ics">Zet je ophaaldagen in je agenda</a>

Koppelcode: van je app naar WhatsApp

Heeft een inwoner jouw afvalapp, en wil hij de seintjes liever in WhatsApp? Met één tik stuur je hem naar Bakkie, met het adres al ingevuld. Hij hoeft niets over te typen, en de code bevat geen naam of telefoonnummer.

Verloop
Inwoner          Jouw app         Jouw server           AfvalBakkie
   |  tikt "Seintjes  |                  |                     |
   |  in WhatsApp"  ->|                  |                     |
   |                  |-- adres -------->|                     |
   |                  |                  |-- POST /koppelcode ->|
   |                  |                  |<- code + link -------|
   |                  |<- link ----------|                     |
   |<- WhatsApp opent met "KOPPEL ABC123"                      |
   |-- verstuurt het bericht ---------------------------------->|
   |<- "Top, je bent aangemeld voor Kerkstraat 12!" ------------|

Aanvraag

VeldTypeUitleg
postcodestringVerplicht. Met of zonder spatie: 1234AB of 1234 AB
huisnummerintegerVerplicht
toevoegingstringOptioneel, bijvoorbeeld A of 2HG
curl
curl -X POST https://www.afval-bakkie.nl/api/v1/koppelcode \
  -H 'Authorization: Bearer abk_…' \
  -H 'Content-Type: application/json' \
  -d '{"postcode":"1234AB","huisnummer":12,"toevoeging":"A"}'
Antwoord
HTTP/1.1 201 Created
Content-Type: application/json

{
  "code": "ABC123",
  "tekst": "KOPPEL ABC123",
  "whatsapp": "https://wa.me/3197006533338?text=KOPPEL%20ABC123",
  "adres": "Kerkstraat 12A, 1234AB Voorbeelddorp",
  "verloopt": "2026-10-01T08:42:00.000Z"
}
StatusBetekenis
201Code aangemaakt. Hij is 30 minuten geldig en werkt één keer. Open whatsapp meteen in je app.
401Geen of geen geldige API-sleutel.
404Het adres staat niet in de BAG.
422Postcode of huisnummer ontbreekt of klopt niet.
429Te veel aanvragen; maximaal 120 per minuut.
503Adressen opzoeken lukt even niet; probeer het zo nog eens.
Foutmelding (altijd een veld "fout" in gewone taal)
{ "fout": "Dit adres staat niet in de BAG. Controleer postcode, huisnummer en toevoeging." }
Node.js (Express)
// Route op jouw server: de app vraagt hier een link op, de sleutel blijft op de server.
app.post('/whatsapp-link', async (req, res) => {
  const { postcode, huisnummer, toevoeging } = req.user.adres; // het adres dat al in je app staat
  const r = await fetch('https://www.afval-bakkie.nl/api/v1/koppelcode', {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.AFVALBAKKIE_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ postcode, huisnummer, toevoeging }),
  });
  if (!r.ok) return res.status(r.status).json(await r.json());
  const { whatsapp } = await r.json();
  res.json({ link: whatsapp }); // de app opent deze link
});

Een API-sleutel vraag je aan via het contactformulier. Je krijgt hem één keer te zien; wij bewaren alleen een controlegetal.

Meldingen per e-mail

Gemeenten kunnen elke melding uit Bakkie laten doorsturen naar hun meldingen- of zaaksysteem. Dat werkt met elk systeem dat e-mail kan inlezen. De gemeente zet dit aan in Beheer → Data → Meldingen → Afhandeling: doorsturen per e-mail. De status zet de gemeente daarna in Beheer; de inwoner krijgt het nieuws in WhatsApp.

De e-mail
From: AfvalBakkie <meldingen@afval-bakkie.nl>
To: meldingen@gemeente.nl
Subject: Melding M-2026-000042 · Container niet geleegd · Kerkstraat 12, 1234AB Voorbeelddorp
X-AfvalBakkie-Referentie: M-2026-000042
X-AfvalBakkie-Soort: niet_geleegd
Content-Type: multipart/mixed

  tekst en HTML: dezelfde gegevens, leesbaar
  bijlage melding.json       (application/json, zie hieronder)
  bijlage melding-foto.jpg   (alleen als de inwoner een foto stuurde)
melding.json
{
  "schema": "afvalbakkie.melding.v1",
  "referentie": "M-2026-000042",
  "soort": "niet_geleegd",
  "soortLabel": "Container niet geleegd",
  "afvalstroom": "gft",
  "omschrijving": "GFT-container staat sinds vanochtend vol aan de straat.",
  "locatie": {
    "tekst": "Kerkstraat 12, 1234AB Voorbeelddorp",
    "lat": 52.2812,
    "lon": 5.159
  },
  "gemeente": {
    "code": "1942",
    "naam": "Voorbeeldgemeente"
  },
  "kanaal": "whatsapp",
  "ontvangen": "2026-10-01T08:12:00.000Z",
  "foto": "melding-foto.jpg",
  "statusBijwerken": "https://www.afval-bakkie.nl/beheer?gemeente=1942"
}
JSON Schema (afvalbakkie.melding.v1)
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.afval-bakkie.nl/schemas/afvalbakkie.melding.v1.json",
  "title": "Melding uit AfvalBakkie (v1)",
  "type": "object",
  "required": [
    "schema",
    "referentie",
    "soort",
    "soortLabel",
    "omschrijving",
    "locatie",
    "kanaal",
    "ontvangen"
  ],
  "properties": {
    "schema": {
      "const": "afvalbakkie.melding.v1"
    },
    "referentie": {
      "type": "string",
      "pattern": "^M-\\d{4}-\\d{6}$",
      "description": "Uniek per melding, ook in het onderwerp en de kopregel X-AfvalBakkie-Referentie"
    },
    "soort": {
      "enum": [
        "niet_geleegd",
        "container_kapot",
        "container_weg",
        "zwerfafval",
        "dumping",
        "ondergrondse_vol",
        "overig"
      ]
    },
    "soortLabel": {
      "type": "string",
      "description": "De soort in gewone taal"
    },
    "afvalstroom": {
      "type": [
        "string",
        "null"
      ],
      "description": "Bijvoorbeeld gft, pmd, papier, rest, textiel of glas"
    },
    "omschrijving": {
      "type": "string",
      "maxLength": 1000
    },
    "locatie": {
      "type": "object",
      "required": [
        "tekst"
      ],
      "properties": {
        "tekst": {
          "type": "string"
        },
        "lat": {
          "type": [
            "number",
            "null"
          ]
        },
        "lon": {
          "type": [
            "number",
            "null"
          ]
        }
      }
    },
    "gemeente": {
      "type": "object",
      "properties": {
        "code": {
          "type": [
            "string",
            "null"
          ],
          "description": "CBS-gemeentecode"
        },
        "naam": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "kanaal": {
      "enum": [
        "whatsapp",
        "app"
      ]
    },
    "ontvangen": {
      "type": "string",
      "format": "date-time"
    },
    "foto": {
      "type": [
        "string",
        "null"
      ],
      "description": "Bestandsnaam van de foto in de bijlagen, of null"
    },
    "statusBijwerken": {
      "type": "string",
      "format": "uri",
      "description": "Link naar de melding in Beheer"
    }
  }
}

Afspraken