NatureImpactNatureImpactZur 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://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)

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
image_urlhttps-AdresseTitelbild aus externer Quelle (max. 1000 Zeichen)

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, baum, pflanze, hecke, magerwiese, ruderalflaeche, feuchtgebiet, trockenmauer, nisthilfe, steinhaufen, sandlinse, totholzhaufen, feuchtbiotop, asthaufen, sonstiges
detailTextSorte, Zielart oder ähnliche Angabe (max. 300 Zeichen)
year_createdGanzzahlPflanz- oder Erstellungsjahr
latitude / longitudeZahlWGS84-Koordinaten
conditionAuswahlgut, pflegebeduerftig, beschaedigt, unbekannt
notesTextNotizen
is_activeJa/NeinNoch vorhanden
image_urlhttps-AdresseFoto aus externer Quelle (max. 1000 Zeichen)

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,
    "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.json

6. Verhalten bei wiederholter Lieferung

  • Gleiche external_id aktualisiert den bestehenden Datensatz – es entstehen keine Duplikate.
  • Nicht mitgesendete optionale Felder bleiben unverändert.
  • Massnahmen eines Einsatzes werden abgeglichen: Eine in der App als done quittierte Massnahme bleibt erledigt, auch wenn die Quelle weiterhin planned sendet. Sendet die Quelle done, 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) mit external_id werden einzeln abgeglichen. Ohne external_id gilt der Block als vollständige Synchronisation: alle früher so gelieferten Massnahmen ohne Einsatz werden ersetzt.
  • Bei Naturwerten und freien Massnahmen kann updated_at mitgeschickt 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 since liefert alles. Danach immer den erhaltenen next_cursor speichern und als since mitschicken (z. B. alle 15 Minuten).
  • Solange has_more true ist, sofort mit dem neuen Cursor weiterholen. limit 1–500 (Standard 200).
  • entities filtert, 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=true erhaltet ihr sie trotzdem.
  • Jeder Eintrag enthält unsere id und – falls vorhanden – eure external_id. Fehlt die external_id, wurde der Eintrag in NatureImpact neu erfasst.
  • Fotos: photo.url ist 24 Stunden gültig. Bitte das Bild herunterladen und bei euch speichern.
  • Gelöschte Einträge stehen in deleted mit Typ, id und external_id.
  • IDs zurückmelden: Beim nächsten Import natureimpact_id plus eure neue external_id mitschicken. 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.