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
- Als Vereins-Admin anmelden und Verein → API öffnen.
- Einen benannten Zugangsschlüssel erstellen und sofort kopieren – er wird nur einmal angezeigt.
- 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_SCHLUESSELBasis-URL: https://natureimpact.app/api/public/v1 · CORS ist für externe Web-Anwendungen offen.
Wichtig: Immer genau diese Adresse verwenden. Andere Adressen leiten weiter, und bei einer Weiterleitung auf eine andere Domain entfernt fetch den Authorization-Header – die Antwort ist dann 401 unauthorized.
3. Endpunkte
Import: POST https://natureimpact.app/api/public/v1/import
Referenz: GET https://natureimpact.app/api/public/v1/reference
Änderungen abrufen: GET https://natureimpact.app/api/public/v1/changes
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", "baum", "pflanze", "hecke", "magerwiese", "ruderalflaeche", "feuchtgebiet", "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)
| Feld | Typ | Beschreibung |
|---|---|---|
external_id | Text (Pflicht) | Eindeutige ID im eigenen System; erneutes Senden aktualisiert den Standort |
name | Text (Pflicht) | Name des Standorts |
site_type | Auswahl | magerwiese, feuchtgebiet, ruderalflaeche, hecke_gehoelz, obstgarten, gewaesser, waldrand, sonstiges |
description | Text | Beschreibung |
area_sqm | Ganzzahl | Fläche in m² |
latitude / longitude | Zahl | WGS84-Koordinaten (Schweiz) |
landowner_partner | Text | Grundeigentümer oder Partner |
focus_species | Liste von Texten | Fokusarten |
is_sensitive | Ja/Nein | Sensible Fläche (Koordinaten werden geschützt dargestellt) |
active | Ja/Nein | Aktiver Standort |
image_url | https-Adresse | Titelbild aus externer Quelle (max. 1000 Zeichen) |
Naturwerte (pois)
| Feld | Typ | Beschreibung |
|---|---|---|
external_id | Text (Pflicht) | Eindeutige ID im eigenen System; erneutes Senden aktualisiert den Naturwert |
site_external_id | Text | Standort, zu dem der Naturwert gehört |
name | Text (Pflicht) | Bezeichnung, z.B. «Nisthilfe Nr. 14» |
kind | Auswahl | obstbaum, baum, pflanze, hecke, magerwiese, ruderalflaeche, feuchtgebiet, trockenmauer, nisthilfe, steinhaufen, sandlinse, totholzhaufen, feuchtbiotop, asthaufen, sonstiges |
detail | Text | Sorte, Zielart oder ähnliche Angabe (max. 300 Zeichen) |
year_created | Ganzzahl | Pflanz- oder Erstellungsjahr |
latitude / longitude | Zahl | WGS84-Koordinaten |
condition | Auswahl | gut, pflegebeduerftig, beschaedigt, unbekannt |
notes | Text | Notizen |
is_active | Ja/Nein | Noch vorhanden |
image_url | https-Adresse | Foto aus externer Quelle (max. 1000 Zeichen) |
Einsätze (events)
| Feld | Typ | Beschreibung |
|---|---|---|
external_id | Text (Pflicht) | Eindeutige ID im eigenen System |
site_external_id | Text | Verweis auf den Standort; weglassen für Einsätze ohne festen Standort |
title | Text (Pflicht) | Titel des Einsatzes |
event_date | Datum (Pflicht) | Format JJJJ-MM-TT |
date_precision | Auswahl | exact, month oder year |
participants_count | Ganzzahl | Anzahl Teilnehmende |
duration_hours | Zahl | Dauer in Stunden |
volunteer_hours | Zahl | Personenstunden; wird aus Teilnehmende × Dauer berechnet, falls nicht geliefert |
evidence_source | Auswahl | protocol, measurement, photo, estimate, memory |
external_url | Text | Link zum Einsatz auf der eigenen Vereins-Website |
is_external_managed | Ja/Nein | Einsatz wird im eigenen System gepflegt |
notes | Text | Notizen |
measures | Liste | Bis zu 50 Massnahmen (siehe unten) |
observations | Liste | Bis zu 50 Artbeobachtungen (siehe unten) |
Massnahmen (measures)
| Feld | Typ | Beschreibung |
|---|---|---|
measure_id | UUID (Pflicht) | ID aus dem Referenz-Endpunkt; unbekannte IDs werden abgelehnt |
status | Auswahl | planned, done oder not_done (Standard: done) |
quantity | Zahl | Menge |
unit | Text | Einheit, z.B. m², Stück, m |
performed_on | Datum | Ausführungsdatum (Standard bei Einsatz-Massnahmen: Einsatzdatum) |
note | Text | Notiz |
site_external_id | Text | Nur im obersten measures-Block: Standort der Massnahme |
poi_external_id | Text | Nur 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)
| Feld | Typ | Beschreibung |
|---|---|---|
species_name | Text (Pflicht) | Name der Art |
taxonomic_group | Auswahl | flora, insects, birds, amphibians_reptiles, mammals, other |
count_estimate | Text | Geschätzte Anzahl, z.B. «ca. 20» |
observation_date | Datum | Format JJJJ-MM-TT (Standard: Einsatzdatum) |
notes | Text | Notizen |
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,
"image_url": "https://verein.ch/bilder/sonnenhalde.jpg"
}],
"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://natureimpact.app/api/public/v1/import \
-H "Authorization: Bearer bm_live_DEIN_SCHLUESSEL" \
-H "Content-Type: application/json" \
-d @import.json6. Verhalten bei wiederholter Lieferung
- Gleiche
external_idaktualisiert den bestehenden Datensatz – es entstehen keine Duplikate. - Nicht mitgesendete optionale Felder bleiben unverändert.
- Massnahmen eines Einsatzes werden abgeglichen: Eine in der App als
donequittierte Massnahme bleibt erledigt, auch wenn die Quelle weiterhinplannedsendet. Sendet die Quelledone, wird das übernommen. Vor Ort erfasste Mengen und Notizen bleiben erhalten. - Artbeobachtungen innerhalb eines Einsatzes ersetzen die bisherigen API-importierten Beobachtungen dieses Einsatzes; manuell erfasste bleiben erhalten.
- Freie Massnahmen (oberster
measures-Block) mitexternal_idwerden einzeln abgeglichen. Ohneexternal_idgilt der Block als vollständige Synchronisation: alle früher so gelieferten Massnahmen ohne Einsatz werden ersetzt. - Bei Naturwerten und freien Massnahmen kann
updated_atmitgeschickt werden: Ist der Stand in NatureImpact neuer, wird nicht überschrieben (Antwort:skipped). - Die Einstellung «Relevant für Statistik» wird nur in der App gesetzt (z. B. für GV, Exkursionen, Feste) und bei Folge-Lieferungen nie überschrieben.
- 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.
Planung: zukünftige Einsätze
- Einsätze mit Datum in der Zukunft dürfen geliefert werden – idealerweise mit Massnahmen im Status
planned. - Sie erscheinen in der App als «Geplant» und dienen am Einsatztag als Vorlage zum Abhaken.
- In Kennzahlen, Jahresbericht und öffentlichem Profil zählen Einsätze erst ab ihrem Datum; geplante Massnahmen zählen nie als umgesetzt.
Bilder
Standorte und Naturwerte akzeptieren image_url – eine öffentlich erreichbare https://-Adresse (max. 1000 Zeichen). Das Bild wird direkt von der Quelle geladen, belastet den Vereinsspeicher nicht und wird beim Ersetzen in der App nie an der Quelle gelöscht.
Änderungen abrufen (Rückkanal)
Mit GET /api/public/v1/changes holt euer System alles ab, was in NatureImpact seit dem letzten Abruf neu erfasst, geändert oder gelöscht wurde: Standorte, Naturwerte (inkl. Foto), Massnahmen, Zustandskontrollen, Einsätze und Artbeobachtungen. Der Schlüssel braucht die Berechtigung «Export».
- Erster Abruf ohne
sinceliefert alles. Danach immer den erhaltenennext_cursorspeichern und alssincemitschicken (z. B. alle 15 Minuten). - Solange
has_moretrueist, sofort mit dem neuen Cursor weiterholen.limit1–500 (Standard 200). entitiesfiltert, z. B.entities=measure,poi. Mögliche Werte: site, poi, measure, poi_check, event, observation.- Änderungen, die über denselben Schlüssel importiert wurden, werden nicht zurückgeschickt (Echo-Schutz). Mit
include_own=trueerhaltet ihr sie trotzdem. - Jeder Eintrag enthält unsere
idund – falls vorhanden – eureexternal_id. Fehlt dieexternal_id, wurde der Eintrag in NatureImpact neu erfasst. - Fotos:
photo.urlist 24 Stunden gültig. Bitte das Bild herunterladen und bei euch speichern. - Gelöschte Einträge stehen in
deletedmit Typ,idundexternal_id. - IDs zurückmelden: Beim nächsten Import
natureimpact_idplus eure neueexternal_idmitschicken. Dann werden beide verknüpft, ohne dass ein Duplikat entsteht. - Bei Konflikten gewinnt die neueste Änderung (über
updated_at).
Abruf
GET /api/public/v1/changes?since=eyJ0cyI6Ij...&entities=measure,poi
Authorization: Bearer bm_live_...
{
"ok": true,
"changes": {
"measures": [{
"id": "7c1e...", "external_id": null,
"measure_id": "3f2a...", "measure_name": "Mahd mit Abtransport",
"status": "done", "quantity": 400, "unit": "m²", "performed_on": "2026-10-02",
"site_external_id": "agn-site-01", "poi_external_id": null,
"updated_at": "2026-10-02T14:03:11.512+00:00"
}],
"pois": [{
"id": "a91b...", "external_id": null, "site_external_id": "agn-site-01",
"name": "Neuer Asthaufen", "kind": "asthaufen", "condition": "gut",
"photo": { "url": "https://.../sign/...", "expires_at": "2026-10-04T14:05:00Z", "source": "natureimpact" },
"updated_at": "2026-10-02T14:05:40.010+00:00"
}]
},
"deleted": [{ "type": "measure", "id": "51d0...", "external_id": "agn-m-77", "deleted_at": "2026-10-02T15:00:00+00:00" }],
"has_more": false,
"next_cursor": "eyJ0cyI6IjIwMjYtMTAt..."
}ID zurückmelden (Import)
POST /api/public/v1/import
{
"pois": [{ "natureimpact_id": "a91b...", "external_id": "agn-poi-311", "name": "Neuer Asthaufen", "site_external_id": "agn-site-01" }],
"measures": [{ "natureimpact_id": "7c1e...", "external_id": "agn-m-912", "measure_id": "3f2a...", "site_external_id": "agn-site-01" }]
}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.403 forbidden– dem Schlüssel fehlt die Berechtigung (Import oder Export).422 import_failed– fachlicher Fehler, z.B. unbekannte Massnahme oder fremder Standort.