Biodiversitäts MonitoringZur App

API-Integrationsleitfaden

Mit dieser Schnittstelle spielen Vereine Standorte und Einsätze direkt aus ihren eigenen Systemen ein. Jeder Zugang gehört genau zu einem Verein – die Vereinszuordnung kommt ausschliesslich aus dem Schlüssel.

1. Zugang erhalten

  1. Als Vereins-Admin anmelden und Verein → API öffnen.
  2. Einen benannten Zugangsschlüssel erstellen und sofort kopieren – er wird nur einmal angezeigt.
  3. Den Schlüssel im eigenen System sicher hinterlegen (nie im Browser-Code oder in öffentlichen Repositories).

Der Widerruf eines Schlüssels wirkt sofort. Schlüssel beginnen mit bm_live_; gespeichert wird nur ein Hash.

2. Authentifizierung

Jede Anfrage trägt den Schlüssel im Authorization-Header:

HTTP-Header

Authorization: Bearer bm_live_DEIN_SCHLUESSEL

Basis-URL: https://deine-app.lovable.app · CORS ist für externe Web-Anwendungen offen.

3. Endpunkte

Import: POST https://deine-app.lovable.app/api/public/v1/import

Referenz: GET https://deine-app.lovable.app/api/public/v1/reference

Der Referenz-Endpunkt liefert die erlaubten Werte und den Massnahmenkatalog des eigenen Vereins inklusive der IDs, die in measures[].measure_id verwendet werden.

Referenz abrufen

GET /api/public/v1/reference
Authorization: Bearer bm_live_DEIN_SCHLUESSEL

HTTP 200

{
  "ok": true,
  "reference": {
    "site_types": ["magerwiese", "feuchtgebiet", "..."],
    "poi_kinds": ["obstbaum", "trockenmauer", "nisthilfe", "steinhaufen", "sandlinse", "totholzhaufen", "feuchtbiotop", "asthaufen", "sonstiges"],
    "poi_conditions": ["gut", "pflegebeduerftig", "beschaedigt", "unbekannt"],
    "date_precisions": ["exact", "month", "year"],
    "evidence_sources": ["protocol", "measurement", "photo", "estimate", "memory"],
    "measure_statuses": ["planned", "done", "not_done"],
    "taxonomic_groups": ["flora", "insects", "birds", "amphibians_reptiles", "mammals", "other"],
    "modules": { "events": true, "pois": true, "observations": true },
    "measures": [
      { "id": "UUID", "category": "Flaechenpflege", "name": "Mähen", "default_unit": "m²" }
    ]
  }
}

4. Unterstützte Felder

Standorte (sites)

FeldTypBeschreibung
external_idText (Pflicht)Eindeutige ID im eigenen System; erneutes Senden aktualisiert den Standort
nameText (Pflicht)Name des Standorts
site_typeAuswahlmagerwiese, feuchtgebiet, ruderalflaeche, hecke_gehoelz, obstgarten, gewaesser, waldrand, sonstiges
descriptionTextBeschreibung
area_sqmGanzzahlFläche in m²
latitude / longitudeZahlWGS84-Koordinaten (Schweiz)
landowner_partnerTextGrundeigentümer oder Partner
focus_speciesListe von TextenFokusarten
is_sensitiveJa/NeinSensible Fläche (Koordinaten werden geschützt dargestellt)
activeJa/NeinAktiver Standort

Naturwerte (pois)

FeldTypBeschreibung
external_idText (Pflicht)Eindeutige ID im eigenen System; erneutes Senden aktualisiert den Naturwert
site_external_idTextStandort, zu dem der Naturwert gehört
nameText (Pflicht)Bezeichnung, z.B. «Nisthilfe Nr. 14»
kindAuswahlobstbaum, trockenmauer, nisthilfe, steinhaufen, sandlinse, totholzhaufen, feuchtbiotop, asthaufen, sonstiges
detailTextSorte, Zielart oder ähnliche Angabe
year_createdGanzzahlPflanz- oder Erstellungsjahr
latitude / longitudeZahlWGS84-Koordinaten
conditionAuswahlgut, pflegebeduerftig, beschaedigt, unbekannt
notesTextNotizen
is_activeJa/NeinNoch vorhanden

Einsätze (events)

FeldTypBeschreibung
external_idText (Pflicht)Eindeutige ID im eigenen System
site_external_idTextVerweis auf den Standort; weglassen für Einsätze ohne festen Standort
titleText (Pflicht)Titel des Einsatzes
event_dateDatum (Pflicht)Format JJJJ-MM-TT
date_precisionAuswahlexact, month oder year
participants_countGanzzahlAnzahl Teilnehmende
duration_hoursZahlDauer in Stunden
volunteer_hoursZahlPersonenstunden; wird aus Teilnehmende × Dauer berechnet, falls nicht geliefert
evidence_sourceAuswahlprotocol, measurement, photo, estimate, memory
external_urlTextLink zum Einsatz auf der eigenen Vereins-Website
is_external_managedJa/NeinEinsatz wird im eigenen System gepflegt
notesTextNotizen
measuresListeBis zu 50 Massnahmen (siehe unten)
observationsListeBis zu 50 Artbeobachtungen (siehe unten)

Massnahmen (measures)

FeldTypBeschreibung
measure_idUUID (Pflicht)ID aus dem Referenz-Endpunkt; unbekannte IDs werden abgelehnt
statusAuswahlplanned, done oder not_done (Standard: done)
quantityZahlMenge
unitTextEinheit, z.B. m², Stück, m
performed_onDatumAusführungsdatum (Standard bei Einsatz-Massnahmen: Einsatzdatum)
noteTextNotiz
site_external_idTextNur im obersten measures-Block: Standort der Massnahme
poi_external_idTextNur im obersten measures-Block: Naturwert der Massnahme

Massnahmen lassen sich innerhalb eines Einsatzes mitliefern oder – ohne Einsatz – im obersten measures-Block direkt an einen Standort oder einen Naturwert hängen.

Artbeobachtungen (observations)

FeldTypBeschreibung
species_nameText (Pflicht)Name der Art
taxonomic_groupAuswahlflora, insects, birds, amphibians_reptiles, mammals, other
count_estimateTextGeschätzte Anzahl, z.B. «ca. 20»
observation_dateDatumFormat JJJJ-MM-TT (Standard: Einsatzdatum)
notesTextNotizen

5. Beispiel: Import

Anfrage

POST /api/public/v1/import
Authorization: Bearer bm_live_DEIN_SCHLUESSEL
Content-Type: application/json

{
  "sites": [{
    "external_id": "agn-site-01",
    "name": "Magerwiese Sonnenhalde",
    "site_type": "magerwiese",
    "description": "Extensiv genutzte Wiese am Südhang",
    "area_sqm": 2400,
    "latitude": 47.242,
    "longitude": 8.723,
    "landowner_partner": "Gemeinde Muster",
    "focus_species": ["Kuckuckslichtnelke", "Wiesenschaumkraut"],
    "is_sensitive": false,
    "active": true
  }],
  "pois": [{
    "external_id": "agn-poi-14",
    "site_external_id": "agn-site-01",
    "name": "Nisthilfe Nr. 14",
    "kind": "nisthilfe",
    "detail": "Zielart: Wendehals",
    "year_created": 2021,
    "latitude": 47.2421,
    "longitude": 8.7233,
    "condition": "gut",
    "is_active": true
  }],
  "events": [{
    "external_id": "agn-event-2026-01",
    "site_external_id": "agn-site-01",
    "title": "Frühlingspflege",
    "event_date": "2026-03-21",
    "date_precision": "exact",
    "participants_count": 8,
    "duration_hours": 3,
    "evidence_source": "protocol",
    "external_url": "https://verein.ch/einsaetze/2026-01",
    "notes": "Neophyten entfernt",
    "measures": [{
      "measure_id": "UUID_AUS_DEM_REFERENZ_ENDPUNKT",
      "status": "done",
      "quantity": 2400,
      "unit": "m²",
      "note": "Erste Mahd"
    }],
    "observations": [{
      "species_name": "Kuckuckslichtnelke",
      "taxonomic_group": "flora",
      "count_estimate": "ca. 200",
      "observation_date": "2026-03-21",
      "notes": "Auf der ganzen Fläche"
    }]
  }],
  "measures": [{
    "measure_id": "UUID_AUS_DEM_REFERENZ_ENDPUNKT",
    "poi_external_id": "agn-poi-14",
    "status": "done",
    "performed_on": "2026-02-10",
    "note": "Nisthilfe gereinigt"
  }]
}

Antwort bei Erfolg

HTTP 200

{
  "ok": true,
  "result": {
    "sites": { "created": 1, "updated": 0, "ids": { "agn-site-01": "INTERNE_UUID" } },
    "pois": { "created": 1, "updated": 0, "ids": { "agn-poi-14": "INTERNE_UUID" } },
    "events": { "created": 1, "updated": 0, "ids": { "agn-event-2026-01": "INTERNE_UUID" } },
    "measures": { "created": 2 }
  }
}

Antwort bei ungültigen Daten

HTTP 400

{
  "error": {
    "code": "validation_error",
    "message": "Die gelieferten Daten sind ungültig",
    "fields": [
      { "path": "events.0.event_date", "message": "Invalid date" }
    ]
  }
}

Aufruf mit curl

curl -X POST https://DEINE-APP.lovable.app/api/public/v1/import \
  -H "Authorization: Bearer bm_live_DEIN_SCHLUESSEL" \
  -H "Content-Type: application/json" \
  -d @import.json

6. Verhalten bei wiederholter Lieferung

  • Gleiche external_id aktualisiert den bestehenden Datensatz – es entstehen keine Duplikate.
  • Nicht mitgesendete optionale Felder bleiben unverändert.
  • Mitgesendete measures- oder observations-Listen innerhalb eines Einsatzes ersetzen die bisherigen API-importierten Untereinträge dieses Einsatzes vollständig; leere Listen löschen sie bewusst. Manuell in der App erfasste Untereinträge bleiben erhalten.
  • Der oberste measures-Block ist eine vollständige Synchronisation: Alle zuvor per API gelieferten Massnahmen ohne Einsatz werden ersetzt.
  • Jeder Block ist eigenständig – ein Verein kann nur Standorte und Naturwerte liefern, nur Einsätze synchronisieren oder nur Massnahmen ergänzen.
  • Jede Anfrage wird vollständig oder gar nicht übernommen – ein Fehler hinterlässt keine Teildaten.

7. Grenzen und Fehlercodes

  • Maximal 100 Standorte, 500 Naturwerte, 100 Einsätze und 500 freie Massnahmen pro Anfrage, je 50 Massnahmen und 50 Beobachtungen pro Einsatz.
  • 401 unauthorized – Schlüssel fehlt, ist ungültig oder wurde widerrufen.
  • 400 invalid_json – die Anfrage enthält kein gültiges JSON.
  • 400 validation_error – ungültige Felder; die Antwort nennt jeden Feldpfad.
  • 400 empty_payload – kein einziger Datensatz mitgesendet.
  • 422 import_failed – fachlicher Fehler, z.B. unbekannte Massnahme oder fremder Standort.