# Ladehase > Ladehase lässt Betreibende kleiner oder privater Ladestationen Gäste per QR-Code oder Terminal PAX IM30 bezahlen. Gratis für Betreibende, das Geld fliesst direkt auf ihr Konto bei zahls.ch oder Freika. Ein Service der siebenberge gmbh. Website: https://ladehase-web.vercel.app/de · API: https://api.ladehase.com/v1 · MCP: https://api.ladehase.com/mcp · OpenAPI: https://ladehase-web.vercel.app/openapi.json # Authentifizierung > API-Schlüssel für Test- und Livedaten, Berechtigungen, Ratenlimits und sicherer Umgang. Jede Anfrage an `/v1` und an den MCP-Server braucht einen API-Schlüssel in der Kopfzeile `Authorization`: ```http GET /v1/organization HTTP/1.1 Host: api.ladehase.com Authorization: Bearer lh_live_… ``` ## Test- und Liveschlüssel
PräfixDatenZahlungenLadestationen
`lh_test_`Testdaten (`livemode: false`)Testzahlungen ohne echtes Geldsimulierte Testgeräte
`lh_live_`Livedaten (`livemode: true`)echte Zahlungen über zahls.ch oder Freikadeine verbundenen Ladestationen
Test- und Livedaten sind vollständig getrennt. Ein Testschlüssel sieht nie Livedaten und umgekehrt. ## Schlüssel erstellen Im Dashboard unter «Entwickler» erstellst du Schlüssel und gibst ihnen einen Namen, damit du sie später wiedererkennst. Der vollständige Schlüssel wird nur einmal angezeigt. Speichere ihn in einem Passwortmanager oder in den Umgebungsvariablen deiner Anwendung. > **Achtung:** Behandle Liveschlüssel wie Passwörter. Leg sie nie in den Quellcode, in Frontends oder in öffentliche Repositorys. Ist ein Schlüssel kompromittiert, widerrufe ihn sofort im Dashboard. ## Ratenlimit Pro Schlüssel gilt ein Limit pro Minute (Standard 600 Anfragen). Jede Antwort enthält die Kopfzeilen `RateLimit-Limit`, `RateLimit-Remaining` und `RateLimit-Reset`. Wird das Limit überschritten, antwortet die API mit `429` und dem Code `rate_limited`. Die Kopfzeile `Retry-After` sagt, wie viele Sekunden du warten sollst. ## Organisationen Ein Schlüssel gehört immer zu genau einer Organisation. Arbeitest du für mehrere Organisationen (zum Beispiel als Installateurin oder Installateur), erstellst du pro Organisation einen eigenen Schlüssel. ## Fehler bei der Anmeldung
StatusCodeBedeutung
401`unauthorized`Schlüssel fehlt, ist ungültig oder widerrufen.
403`forbidden`Der Schlüssel darf diese Aktion nicht ausführen oder die Funktion ist für die Organisation nicht freigeschaltet.
--- # Einstieg > In fünf Minuten zur ersten Anfrage an die Ladehase API, mit Testgeräten und ohne Hardware. Die Ladehase API ist eine REST-API unter `https://api.ladehase.com/v1`. Alles, was im Dashboard geht, geht auch über die API: Standorte und Ladepunkte verwalten, Tarife festlegen, Ladungen starten und stoppen, Sitzungen auswerten, Zugänge anlegen und Webhooks empfangen. ## Was du brauchst - Ein Konto im [Dashboard](https://dashboard.ladehase.com/registrieren). Es ist gratis. - Einen API-Schlüssel. Du erstellst ihn im Dashboard unter «Entwickler». Für den Anfang nimmst du einen Testschlüssel `lh_test_…`. - `curl` oder eine beliebige Programmiersprache. ## Testgeräte anlegen Im Testmodus brauchst du keine echte Ladestation. Lege simulierte Ladepunkte an: ```bash curl -X POST https://api.ladehase.com/v1/integrations/test \ -H "Authorization: Bearer lh_test_…" ``` Danach listest du die Geräte auf: ```bash curl https://api.ladehase.com/v1/devices \ -H "Authorization: Bearer lh_test_…" ``` ```json { "object": "list", "data": [ { "id": "dev_…", "object": "device", "name": "Testladepunkt 1", "livemode": false } ], "has_more": false } ``` ## Eine Ladung starten Eine Sitzung ohne Zahlung startest du mit `POST /v1/sessions`. So testest du den Ablauf, ohne die Bezahlseite zu öffnen: ```bash curl -X POST https://api.ladehase.com/v1/sessions \ -H "Authorization: Bearer lh_test_…" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 6f1c2a1e-erste-ladung" \ -d '{ "device_id": "dev_…" }' ``` Steck danach im Testmodus ein simuliertes Auto ein: ```bash curl -X POST https://api.ladehase.com/v1/devices/dev_…/simulate \ -H "Authorization: Bearer lh_test_…" \ -H "Content-Type: application/json" \ -d '{ "action": "plug", "vehicle_need_wh": 12000 }' ``` Die Sitzung wechselt auf `charging`. Mit `GET /v1/sessions/ses_…` siehst du Energie und laufenden Betrag. Stoppen kannst du mit `POST /v1/sessions/ses_…/stop`. ## Grundsätze der API - **Einheiten:** Beträge sind Ganzzahlen in Rappen (`1250` = CHF 12.50), Energie in Wattstunden (`12400` = 12.4 kWh), Energiepreise in Hundertstel-Rappen pro kWh (`4500` = 45 Rappen pro kWh). Mehr dazu unter [Tarife](/de/docs/tarife). - **IDs** haben ein Präfix: `ses_` für Sitzungen, `dev_` für Geräte, `loc_` für Standorte und so weiter. - **Listen** liefern `object`, `data` und `has_more`. Blättern mit `limit` (höchstens 100) und `starting_after`. - **Zeitpunkte** sind ISO 8601 in UTC. - **Fehler** folgen RFC 9457 (Problem Details). Siehe [Fehler](/de/docs/fehler). - **Wiederholbar:** Schreibende Anfragen akzeptieren eine Kopfzeile `Idempotency-Key`. Dieselbe Anfrage mit demselben Schlüssel wirkt innert 24 Stunden nur einmal. ## Wie weiter - [Authentifizierung](/de/docs/authentifizierung) und Schlüsselverwaltung - [Sitzungen und Zustände](/de/docs/sitzungen) verstehen - [Webhooks](/de/docs/webhooks) empfangen - [MCP-Server](/de/docs/mcp) mit Claude, ChatGPT oder Cursor nutzen - [API-Referenz](/de/docs/api) mit allen Endpunkten --- # Fehler > Fehlerantworten nach RFC 9457 (Problem Details) und alle stabilen Fehlercodes der Ladehase API. Fehler beantwortet die API mit einem passenden HTTP-Status und einem Körper im Format `application/problem+json` nach RFC 9457: ```json { "type": "https://www.ladehase.com/de/docs/fehler#invalid_state", "title": "Zustand passt nicht", "status": 409, "code": "invalid_state", "detail": "Die Sitzung ist bereits abgerechnet." } ``` - `code` ist stabil und für Programme gedacht. Reagiere in deinem Code auf `code`, nicht auf `title` oder `detail`. - `title` und `detail` sind für Menschen und können sich ändern. - `type` verweist auf den passenden Abschnitt dieser Seite. ## Codes ### `invalid_request` Status 400. Die Anfrage ist ungültig, zum Beispiel fehlt ein Pflichtfeld oder ein Wert liegt ausserhalb des erlaubten Bereichs. `detail` nennt das Feld und den Grund. ### `unauthorized` Status 401. Der API-Schlüssel fehlt, ist ungültig oder widerrufen. Siehe [Authentifizierung](/de/docs/authentifizierung). ### `forbidden` Status 403. Der Schlüssel oder die Person darf diese Aktion nicht ausführen. ### `not_found` Status 404. Das Objekt gibt es nicht oder es gehört zu einer anderen Organisation beziehungsweise zum anderen Modus (Test oder live). ### `conflict` Status 409. Die Anfrage widerspricht dem aktuellen Stand, zum Beispiel läuft am Anschluss bereits eine Sitzung. ### `invalid_state` Status 409. Die Aktion ist im aktuellen Zustand nicht möglich, zum Beispiel eine Rückerstattung vor der Abrechnung. Siehe [Sitzungen und Zustände](/de/docs/sitzungen). ### `device_unavailable` Status 409. Der Ladepunkt ist offline, gesperrt, gestört oder besetzt. ### `device_error` Status 502. Die Ladestation oder die Cloud des Herstellers hat einen Befehl nicht ausgeführt. Versuch es später nochmals. ### `payment_error` Status 409 oder 502. Der Zahlungspartner hat die Aktion abgelehnt oder ist nicht erreichbar. ### `integration_error` Status 502. Die Verbindung zum Hersteller oder Zahlungspartner ist gestört, zum Beispiel weil Zugangsdaten abgelaufen sind. Prüf die Verbindung im Dashboard. ### `not_supported` Status 400 oder 409. Die Funktion wird für diesen Hersteller, dieses Gerät oder diesen Zahlungspartner nicht unterstützt. ### `idempotency_conflict` Status 422. Derselbe `Idempotency-Key` wurde mit anderem Inhalt verwendet. Nimm für jede neue Anfrage einen neuen Schlüssel. ### `idempotency_in_progress` Status 409. Eine Anfrage mit demselben `Idempotency-Key` wird noch verarbeitet. Warte kurz und wiederhole. ### `rate_limited` Status 429. Zu viele Anfragen. Die Kopfzeile `Retry-After` sagt, wie viele Sekunden du warten sollst. ### `internal_error` Status 500. Ein unerwarteter Fehler bei uns. Wir werden automatisch benachrichtigt. Wiederhole die Anfrage mit demselben `Idempotency-Key`. ## Richtig reagieren - Bei `429`, `502` und `500` lohnt sich eine Wiederholung mit steigender Wartezeit. - Bei `400`, `401`, `403`, `404` und `422` ändert eine Wiederholung nichts. Korrigiere die Anfrage. - Bei `409` liest du das Objekt neu und entscheidest anhand des Zustands. --- # MCP-Server für KI-Agenten > Ladehase mit Claude, ChatGPT oder Cursor verbinden. Werkzeuge für Standorte, Geräte, Ladungen, Tarife und Berichte. Ladehase bietet einen MCP-Server (Model Context Protocol). Damit können KI-Agenten wie Claude, ChatGPT oder Cursor deine Ladestationen abfragen, Tarife vorschlagen, Ladungen starten oder Berichte erstellen. Die Werkzeuge entstehen aus denselben Endpunkten wie die REST-API. - **Adresse:** `https://api.ladehase.com/mcp` - **Transport:** Streamable HTTP (JSON-RPC über `POST`) - **Anmeldung:** `Authorization: Bearer lh_live_…` oder `lh_test_…` > **Hinweis:** Beginne mit einem Testschlüssel. So kann der Agent alles ausprobieren, ohne echte Ladestationen zu steuern oder Geld zu bewegen. ## Claude In Claude Desktop öffnest du die Einstellungen unter «Entwickler» und bearbeitest die Konfiguration: ```json { "mcpServers": { "ladehase": { "command": "npx", "args": ["-y", "mcp-remote", "https://api.ladehase.com/mcp", "--header", "Authorization:Bearer lh_test_…"] } } } ``` In Claude Code genügt ein Befehl: ```bash claude mcp add --transport http ladehase https://api.ladehase.com/mcp --header "Authorization: Bearer lh_test_…" ``` ## ChatGPT In ChatGPT fügst du unter «Einstellungen», «Connectors» einen eigenen Connector hinzu (Entwicklermodus). Als Adresse gibst du `https://api.ladehase.com/mcp` ein und als Authentifizierung den API-Schlüssel als Bearer-Token. ## Cursor Leg im Projekt die Datei `.cursor/mcp.json` an: ```json { "mcpServers": { "ladehase": { "url": "https://api.ladehase.com/mcp", "headers": { "Authorization": "Bearer lh_test_…" } } } } ``` ## Werkzeuge Der Server liefert mit `tools/list` alle Werkzeuge mit Beschreibung und JSON-Schema. Die Namen entsprechen den Endpunkten der [API-Referenz](/de/docs/api) in Kleinbuchstaben mit Unterstrich, zum Beispiel: - `list_devices`, `get_device_state`: Ladepunkte und ihr Zustand - `create_session`, `stop_session`, `list_sessions`: Ladungen starten, stoppen, auswerten - `preview_tariff_draft`, `create_tariff`: Tarife berechnen und anlegen - `create_code`, `render_signs`: QR-Codes und Schilder - `get_report_summary`: Kennzahlen - `create_access_grant`: Zugänge für Bewohnende oder Mitarbeitende Lesende Werkzeuge sind als `readOnlyHint` markiert, verändernde Werkzeuge wie Stoppen, Sperren oder Rückerstatten als `destructiveHint`. Gute Agenten fragen vor solchen Aktionen nach. ## Beispiel ```bash curl -X POST https://api.ladehase.com/mcp \ -H "Authorization: Bearer lh_test_…" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_devices", "arguments": {} } }' ``` ## Einheiten für Agenten Der Server teilt Agenten beim Verbinden mit, dass Beträge in Rappen, Energie in Wh und Energiepreise in Hundertstel-Rappen pro kWh angegeben sind. Gib diese Regel in eigenen Anweisungen trotzdem nochmals mit, wenn ein Agent Tarife anlegen soll. ## Schreibende Werkzeuge abschalten Schreibende Werkzeuge lassen sich für eine Organisation deaktivieren. Der Agent kann dann nur noch lesen und erhält bei verändernden Aufrufen eine verständliche Meldung. Schreib uns an [developer@ladehase.com](mailto:developer@ladehase.com), wenn du das für deine Organisation möchtest. --- # SDK für TypeScript > Typisierter Client für die Ladehase API, Live-Updates per Server-Sent Events und Prüfung von Webhooks. Das SDK `@ladehase/sdk` ist ein schlanker, vollständig typisierter Client. Es wird aus der OpenAPI-Beschreibung erzeugt und passt darum immer zur API. ## Installation ```bash npm install @ladehase/sdk ``` ## Client erstellen ```ts import { ladehase } from "@ladehase/sdk"; const lh = ladehase({ apiKey: process.env.LADEHASE_KEY! }); const { data, error } = await lh.GET("/v1/devices"); if (error) throw new Error(error.title); for (const device of data.data) console.log(device.id, device.name); ``` Pfade, Parameter und Antworten sind typisiert. Dein Editor schlägt Endpunkte und Felder vor. ## Ladung starten und stoppen ```ts const { data: session } = await lh.POST("/v1/sessions", { body: { device_id: "dev_…" }, headers: { "idempotency-key": crypto.randomUUID() }, }); await lh.POST("/v1/sessions/{id}/stop", { params: { path: { id: session!.id } } }); ``` ## Tarif in der Vorschau berechnen ```ts const { data: preview } = await lh.POST("/v1/tariffs/preview", { body: { rules: { energyPrice: 4500, serviceFee: 50 }, energy_wh: [20000] }, }); ``` ## Webhooks prüfen ```ts import { verifyWebhook } from "@ladehase/sdk"; const ok = await verifyWebhook(rawBody, signatureHeader, process.env.LADEHASE_WEBHOOK_SECRET!); ``` Die Funktion nutzt Web Crypto und läuft in Node.js, Deno, Bun und Edge-Umgebungen. Details unter [Webhooks](/de/docs/webhooks). ## Andere Sprachen Für andere Sprachen erzeugst du einen Client aus der OpenAPI-Beschreibung unter [www.ladehase.com/openapi.json](/openapi.json), zum Beispiel mit openapi-generator. Oder du nutzt die REST-API direkt mit einem HTTP-Client deiner Wahl. --- # Sitzungen und Zustände > Wie eine Ladung vom Scan bis zur Abrechnung verläuft, welche Zustände es gibt und wann eine Sitzung endet. Eine **Sitzung** (`ses_…`) ist eine Ladung von der Zahlung oder Freigabe bis zur Abrechnung. Sie gehört zu genau einem Ladepunkt und friert beim Start die gültige Tarifversion ein. Spätere Tarifänderungen wirken sich nicht auf laufende Sitzungen aus. ## Ablauf 1. Die fahrende Person scannt den QR-Code oder wählt am Terminal den Ladepunkt. Ladehase legt eine Sitzung an (`created`). 2. Sie reserviert einen Betrag oder kauft ein Paket (`awaiting_payment`). 3. Ist die Zahlung autorisiert (`authorized`), gibt Ladehase den Ladepunkt frei und wartet auf das Auto (`waiting_for_vehicle`). 4. Das Auto lädt (`charging`). Ladehase misst laufend die Energie und rechnet den Betrag hoch. 5. Das Kabel wird abgezogen oder die Ladung gestoppt (`completed`). 6. Ladehase belastet den Endbetrag, gibt den Rest der Reservation frei und stellt die Quittung aus (`settled`). ## Zustände
ZustandBedeutung
`created`Sitzung angelegt, noch keine Zahlung.
`awaiting_payment`Zahlung läuft beim Zahlungspartner.
`authorized`Bezahlt oder per Zugang freigegeben.
`waiting_for_vehicle`Ladepunkt freigegeben, Auto noch nicht eingesteckt oder lädt noch nicht.
`charging`Das Auto lädt.
`suspended`Ladung pausiert, zum Beispiel durch das Auto oder das Lastmanagement.
`idle`Auto voll, Kabel steckt noch. Hier kann eine Standzeitgebühr anfallen.
`stopping`Stopp angefordert, Ladehase wartet auf die Bestätigung der Ladestation.
`completed`Ladung beendet, Abrechnung folgt.
`settled`Abgerechnet. Endzustand.
`payment_failed`Zahlung fehlgeschlagen. Endzustand.
`expired`Kein Auto innert der Frist eingesteckt, Reservation freigegeben. Endzustand.
`canceled`Abgebrochen, Reservation freigegeben. Endzustand.
`error`Störung. Wird behoben (weiter zu `completed`) oder abgebrochen (`canceled`).
Pro Anschluss gibt es höchstens eine aktive Sitzung. Eine zweite Anfrage für denselben Anschluss beantwortet die API mit `409` und dem Code `conflict`. ## Gründe für das Ende Das Feld `stop_reason` sagt, warum eine Ladung endete: - `unplugged`: Kabel abgezogen - `amount_reached`: Reservation erreicht (abzüglich Sicherheitsmarge) - `package_reached`: Paket aufgebraucht - `driver`: von der fahrenden Person beendet - `operator`: von den Betreibenden beendet - `api`: über die API beendet - `max_duration`: Höchstdauer erreicht - `device_error`: Störung an der Ladestation ## Abrechnungsarten Das Feld `mode` zeigt, wie abgerechnet wird: - `reservation`: Betrag reservieren, nach Verbrauch belasten, Rest freigeben. - `prepaid`: Paket vorab bezahlt. Nicht genutztes Guthaben wird je nach Einstellung zurückerstattet oder verfällt. - `access`: Ladung über einen Zugang, gratis oder zum Sondertarif. ## Beträge einer Sitzung
FeldBedeutung
`authorized_amount`Reservierter beziehungsweise vorab bezahlter Betrag in Rappen.
`amount`Laufend hochgerechneter Betrag oder Endbetrag in Rappen.
`captured_amount`Tatsächlich belasteter Betrag in Rappen.
`refunded_amount`Zurückerstatteter Betrag in Rappen.
`vat_amount`Enthaltener MWST-Anteil in Rappen.
`energy_wh`Geladene Energie in Wattstunden.
Die Zeitleiste einer Sitzung mit allen Ereignissen liefert `GET /v1/sessions/{id}/events`. --- # Tarife und Einheiten > Preis pro kWh, Startgebühr, Servicegebühr, Standzeitgebühr, Zeitfenster und Höchstbetrag. Mit allen Einheiten. Ein Tarif besteht aus kombinierbaren Bestandteilen. Tarife sind versioniert: Jede Änderung ergibt eine neue Version, laufende Sitzungen behalten ihre Version. ## Einheiten > **Achtung:** Die API rechnet immer mit Ganzzahlen. Achte besonders auf den Energiepreis: Er ist in Hundertstel-Rappen pro kWh angegeben.
GrösseEinheitBeispiel
BeträgeRappen`250` = CHF 2.50
EnergieWattstunden (Wh)`12400` = 12.4 kWh
EnergiepreisHundertstel-Rappen pro kWh`4500` = 45 Rappen pro kWh = 0.45 CHF/kWh
LeistungWatt`11000` = 11 kW
StandzeitgebührRappen pro angefangene Minute`10` = 10 Rappen pro Minute
MWST-SatzBasispunkte`810` = 8.1 %
Gerundet wird erst der Endbetrag: in CHF auf 5 Rappen, in EUR auf 1 Cent. ## Bestandteile - **`energyPrice`**: Preis pro kWh ausserhalb von Zeitfenstern. Pflicht. - **`startFee`**: Startgebühr pro Sitzung in Rappen. - **`serviceFee`**: Servicegebühr der Betreibenden pro Sitzung in Rappen. Sie wird separat ausgewiesen. - **`timeWindows`**: Zeitfenster nach Wochentag (1 = Montag bis 7 = Sonntag) und Uhrzeit mit eigenem Energiepreis, zum Beispiel ein Niedertarif in der Nacht. Die Energie wird anhand der Zählerstände den Fenstern zugeordnet. - **`idleFee`**: Standzeitgebühr mit `pricePerMinute`, `graceMinutes`, `trigger` (`after_charging` ab Ladeende oder `after_start` ab Beginn), optional `maxAmount` und ruhigen Zeiten `quietHours`. - **`maxAmount`**: Höchstbetrag pro Sitzung in Rappen. - **`timeZone`**: Zeitzone für Zeitfenster und ruhige Zeiten, Standard `Europe/Zurich`. ## Tarif anlegen ```bash curl -X POST https://api.ladehase.com/v1/tariffs \ -H "Authorization: Bearer lh_test_…" \ -H "Content-Type: application/json" \ -d '{ "name": "Gäste", "rules": { "energyPrice": 4500, "serviceFee": 50, "timeWindows": [ { "days": [1,2,3,4,5,6,7], "from": "22:00", "to": "06:00", "energyPrice": 3000, "label": "Nacht" } ], "idleFee": { "pricePerMinute": 10, "graceMinutes": 120, "trigger": "after_charging", "quietHours": { "from": "22:00", "to": "07:00" } } } }' ``` ## Vorschau Mit `POST /v1/tariffs/preview` berechnest du für einen Entwurf, was Ladungen mit bestimmten Energiemengen kosten, ohne einen Tarif zu speichern: ```bash curl -X POST https://api.ladehase.com/v1/tariffs/preview \ -H "Authorization: Bearer lh_test_…" \ -H "Content-Type: application/json" \ -d '{ "rules": { "energyPrice": 4500, "serviceFee": 50 }, "energy_wh": [10000, 20000, 40000] }' ``` Für 20 kWh ergibt das 20 × 45 Rappen + 50 Rappen = CHF 9.50. ## MWST Ist die Organisation mehrwertsteuerpflichtig, sind alle Preise inklusive MWST. Die Sitzung weist den Anteil in `vat_amount` aus. --- # Testmodus > Ladungen, Zahlungen und Störungen ohne Hardware und ohne echtes Geld durchspielen. Mit einem Testschlüssel `lh_test_…` arbeitest du in einer vollständig getrennten Testumgebung. Ladestationen sind simuliert, Zahlungen laufen über eine Testzahlung. Ideal zum Ausprobieren, für automatisierte Tests und für KI-Agenten, die du zuerst gefahrlos üben lassen willst. ## Testgeräte anlegen ```bash curl -X POST https://api.ladehase.com/v1/integrations/test \ -H "Authorization: Bearer lh_test_…" ``` Ladehase legt eine Testverbindung mit simulierten Ladepunkten an. Die Anfrage ist wiederholbar, bestehende Testgeräte bleiben erhalten. ## Ein Auto simulieren Mit `POST /v1/devices/{id}/simulate` steuerst du ein Testgerät. Erlaubte Aktionen:
AktionWirkung
`plug`Auto einstecken. Mit `vehicle_need_wh` legst du fest, wie viel Energie das Auto aufnimmt, bis es voll ist.
`unplug`Kabel abziehen. Eine laufende Ladung endet mit dem Grund `unplugged`.
`set_power`Ladeleistung ändern, in Watt mit `power_w`.
`offline`, `online`Verbindung zur Ladestation trennen oder wiederherstellen.
`error`, `clear_error`Störung auslösen oder beheben.
```bash curl -X POST https://api.ladehase.com/v1/devices/dev_…/simulate \ -H "Authorization: Bearer lh_test_…" \ -H "Content-Type: application/json" \ -d '{ "action": "plug", "vehicle_need_wh": 20000 }' ``` ## Testzahlungen Scannst du im Testmodus den QR-Code eines Testladepunkts, öffnet die Bezahlseite eine Testzahlung. Dort wählst du, ob die Zahlung gelingt, fehlschlägt oder abgebrochen wird. So prüfst du alle Wege, ohne Geld zu bewegen. > **Hinweis:** Webhooks funktionieren im Testmodus genauso wie live. Jedes Ereignis trägt `livemode: false`. ## Live schalten Sobald du eine echte Ladestation und ein Konto bei zahls.ch oder Freika verbunden hast, nutzt du einen Liveschlüssel `lh_live_…`. Am Code ändert sich nichts ausser dem Schlüssel. --- # Webhooks > Ereignisse zu Sitzungen, Zahlungen und Geräten an dein System senden lassen, mit Signaturprüfung. Webhooks melden Ereignisse an eine HTTPS-Adresse deiner Wahl, zum Beispiel wenn eine Ladung beginnt oder abgerechnet ist. So musst du die API nicht ständig abfragen. ## Endpunkt anlegen ```bash curl -X POST https://api.ladehase.com/v1/webhook_endpoints \ -H "Authorization: Bearer lh_test_…" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.ch/webhooks/ladehase", "events": ["session.settled", "device.offline"] }' ``` Die Antwort enthält das Geheimnis `secret` für die Signaturprüfung. Es wird nur in dieser Antwort angezeigt. Ohne `events` erhältst du alle Ereignisse (`["*"]`). ## Ereignisse
TypWann
`session.created`Sitzung angelegt
`session.started`Ladung hat begonnen
`session.stopped`Ladung beendet
`session.settled`Sitzung abgerechnet, Quittung erstellt
`session.failed`Zahlung fehlgeschlagen, verfallen oder abgebrochen
`payment.authorized`Betrag reserviert oder Paket bezahlt
`payment.captured`Endbetrag belastet
`payment.released`Reservation freigegeben
`payment.refunded`Rückerstattung ausgeführt
`device.online`, `device.offline`, `device.error`Zustand eines Ladepunkts hat sich geändert
`terminal.online`, `terminal.offline`Zustand eines Terminals hat sich geändert
`integration.error`Verbindung zu Hersteller oder Zahlungspartner gestört
`session.updated` wird nur live an das Dashboard verteilt und nicht als Webhook versendet, weil es während einer Ladung sehr häufig vorkommt. ## Aufbau ```http POST /webhooks/ladehase HTTP/1.1 Content-Type: application/json User-Agent: Ladehase-Webhooks/1.0 Ladehase-Event: session.settled Ladehase-Signature: t=1792958400,v1=5f2b9c… ``` ```json { "id": "evt_…", "object": "event", "type": "session.settled", "livemode": true, "created_at": "2026-10-25T08:30:00.000Z", "data": { "id": "ses_…", "object": "session", "state": "settled", "captured_amount": 950 } } ``` ## Signatur prüfen Die Kopfzeile `Ladehase-Signature` enthält einen Zeitstempel `t` und die Signatur `v1`. `v1` ist ein HMAC-SHA256 über `t` + `.` + den unveränderten Rohtext der Anfrage, mit dem Geheimnis des Endpunkts als Schlüssel, hexadezimal codiert. 1. Lies den Rohtext der Anfrage, bevor du ihn als JSON verarbeitest. 2. Berechne `HMAC_SHA256(secret, t + "." + rohtext)`. 3. Vergleiche das Ergebnis zeitkonstant mit `v1`. 4. Lehne Anfragen ab, deren `t` mehr als 5 Minuten von der aktuellen Zeit abweicht. Mit dem SDK geht das in einer Zeile: ```ts import { verifyWebhook } from "@ladehase/sdk"; export async function POST(request: Request) { const body = await request.text(); const ok = await verifyWebhook(body, request.headers.get("ladehase-signature") ?? "", process.env.LADEHASE_WEBHOOK_SECRET!); if (!ok) return new Response("Signatur ungültig", { status: 400 }); const event = JSON.parse(body); // Ereignis verarbeiten … return new Response(null, { status: 204 }); } ``` Ohne SDK, in Node.js: ```ts import { createHmac, timingSafeEqual } from "node:crypto"; function verify(body: string, header: string, secret: string) { const parts = Object.fromEntries(header.split(",").map((p) => p.split("="))); const t = Number(parts.t); if (!parts.v1 || Math.abs(Date.now() / 1000 - t) > 300) return false; const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest(); const given = Buffer.from(parts.v1, "hex"); return given.length === expected.length && timingSafeEqual(given, expected); } ``` > **Hinweis:** Vertrau dem Inhalt eines Webhooks nicht blind. Lies bei wichtigen Entscheiden die Sitzung mit `GET /v1/sessions/{id}` nach. ## Antwort und Wiederholungen Antworte innert 10 Sekunden mit einem Status 2xx. Andernfalls versucht Ladehase es erneut: nach 1 Minute, 5 Minuten, 30 Minuten, 2, 6, 12, 24 und nochmals 24 Stunden, insgesamt rund 3 Tage. Ereignisse können doppelt ankommen. Prüfe darum die `id` und verarbeite jedes Ereignis nur einmal. Das Zustellprotokoll siehst du mit `GET /v1/webhook_endpoints/{id}/deliveries`. Einzelne Ereignisse sendest du mit `POST /v1/events/{id}/resend` erneut. --- # API-Referenz > Öffentliche API von Ladehase: Ladestationen verbinden, Tarife festlegen, Ladungen starten und stoppen, abrechnen, auswerten. Alles, was im Dashboard geht, geht auch hier. Beträge in Rappen, Energie in Wattstunden, Energiepreise in Hundertstel-Rappen pro kWh. Basis-URL: `https://api.ladehase.com`. Anmeldung: `Authorization: Bearer lh_live_…` oder `lh_test_…`. ## Organisation ### Organisation abrufen `GET /v1/organization` Einstellungen der Organisation dieses API-Schlüssels: Name, Subdomain der Instanz, MWST, Branding, Testmodus. Antwort 200: ```json { "id": "org_9c1f0b7a2e3d4c5b", "object": "organization", "name": "Hof Meier", "slug": "hof-meier", "instance_url": "https://hof-meier.ladehase.com", "test_mode": true, "vat_registered": true, "vat_number": "string", "vat_rate_bps": 0, "accent_color": "string" } ``` ### Organisation ändern `PATCH /v1/organization` Ändert Name, Subdomain (alte Subdomain bleibt als Weiterleitung), Adresse, MWST, Akzentfarbe, Bedingungen für Fahrende oder Sichtbarkeit der Instanzseite. Anfrage: ```json { "name": "string", "slug": "string", "legal_name": "string", "street": "string", "postal_code": "string", "city": "string", "country": "string", "contact_email": "person@example.ch", "contact_phone": "string", "website": "https://example.ch/webhooks/ladehase", "vat_registered": true, "vat_number": "string", "vat_rate_bps": 0, "accent_color": "string", "logo_url": "https://example.ch/webhooks/ladehase", "welcome_text": { "de": "string", "fr": "string", "it": "string", "en": "string" }, "driver_terms": { "de": "string", "fr": "string", "it": "string", "en": "string" }, "public_page": { "showLocations": true, "showTariffs": true, "showMap": true, "showContact": true, "showOpeningHours": true }, "test_mode": true } ``` Antwort 200: ```json { "id": "org_9c1f0b7a2e3d4c5b", "object": "organization", "name": "Hof Meier", "slug": "hof-meier", "instance_url": "https://hof-meier.ladehase.com", "test_mode": true, "vat_registered": true, "vat_number": "string", "vat_rate_bps": 0, "accent_color": "string" } ``` ## Verbindungen ### Verbindungen auflisten `GET /v1/integrations` Verbundene Hersteller (Easee) und Zahlungskonten (zahls.ch, Freika). Zugangsdaten werden nie ausgegeben. Antwort 200: ```json { "object": "list", "data": [ { "id": "int_4a7c2b9d0e1f3a5c", "object": "integration", "kind": "easee", "livemode": true, "name": "string", "status": "pending", "public_info": {}, "webhook_url": "string", "last_error": "string", "last_checked_at": "2026-10-25T08:30:00.000Z", "last_synced_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ], "has_more": true } ``` ### Easee verbinden `POST /v1/integrations/easee` Meldet sich einmal mit dem Easee-Login an. Ladehase speichert nur die Tokens (verschlüsselt), nie das Passwort, und importiert sofort alle Charger. Neue Charger sind zuerst deaktiviert. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "userName": "string", "password": "string" } ``` Antwort 201: ```json { "id": "int_4a7c2b9d0e1f3a5c", "object": "integration", "kind": "easee", "livemode": true, "name": "string", "status": "pending", "public_info": {}, "webhook_url": "string", "last_error": "string", "last_checked_at": "2026-10-25T08:30:00.000Z", "last_synced_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### zahls.ch-Konto verbinden `POST /v1/integrations/zahls` Verbindet das eigene zahls.ch-Konto mit Instanzname und API-Schlüssel. Das Geld fliesst direkt auf dieses Konto. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "instance": "string", "apiKey": "string" } ``` Antwort 201: ```json { "id": "int_4a7c2b9d0e1f3a5c", "object": "integration", "kind": "easee", "livemode": true, "name": "string", "status": "pending", "public_info": {}, "webhook_url": "string", "last_error": "string", "last_checked_at": "2026-10-25T08:30:00.000Z", "last_synced_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Freika-Konto verbinden `POST /v1/integrations/freika` Verbindet das eigene Freika-Konto mit einem API-Schlüssel (Rechte «schreiben» und «Terminals»). Nötig für das PAX IM30. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "apiKey": "string" } ``` Antwort 201: ```json { "id": "int_4a7c2b9d0e1f3a5c", "object": "integration", "kind": "easee", "livemode": true, "name": "string", "status": "pending", "public_info": {}, "webhook_url": "string", "last_error": "string", "last_checked_at": "2026-10-25T08:30:00.000Z", "last_synced_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Testgeräte anlegen `POST /v1/integrations/test` Legt simulierte Ladepunkte und Testzahlungen an. Ideal zum Ausprobieren ohne Hardware und für automatisierte Tests (Schlüssel lh_test_). Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 201: ```json { "id": "int_4a7c2b9d0e1f3a5c", "object": "integration", "kind": "easee", "livemode": true, "name": "string", "status": "pending", "public_info": {}, "webhook_url": "string", "last_error": "string", "last_checked_at": "2026-10-25T08:30:00.000Z", "last_synced_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Verbindung prüfen `POST /v1/integrations/{id}/test` Prüft die Zugangsdaten beim Hersteller oder Zahlungspartner und aktualisiert den Status. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 200: ```json { "id": "int_4a7c2b9d0e1f3a5c", "object": "integration", "kind": "easee", "livemode": true, "name": "string", "status": "pending", "public_info": {}, "webhook_url": "string", "last_error": "string", "last_checked_at": "2026-10-25T08:30:00.000Z", "last_synced_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Geräte importieren `POST /v1/integrations/{id}/sync` Liest alle Geräte beim Hersteller und legt neue Standorte, Geräte, Ladepunkte und QR-Codes an. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 200: ```json {} ``` ### Verbindung trennen `DELETE /v1/integrations/{id}` Löscht die Zugangsdaten. Geräte dieses Herstellers werden deaktiviert. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "int_4a7c2b9d0e1f3a5c", "object": "integration", "kind": "easee", "livemode": true, "name": "string", "status": "pending", "public_info": {}, "webhook_url": "string", "last_error": "string", "last_checked_at": "2026-10-25T08:30:00.000Z", "last_synced_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ## Standorte ### Standorte auflisten `GET /v1/locations` Alle Standorte der Organisation. Antwort 200: ```json { "object": "list", "data": [ { "id": "loc_2b8e1c0d9f7a6b5c", "object": "location", "name": "Hofladen", "street": "string", "postal_code": "string", "city": "string", "country": "string", "latitude": 0, "longitude": 0, "time_zone": "string", "opening_hours": "string", "payment_integration_id": "string", "is_public": true, "created_at": "string" } ], "has_more": true } ``` ### Standort anlegen `POST /v1/locations` Neuer Standort mit Adresse und Zahlungskonto für QR-Zahlungen. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "name": "string", "street": "string", "postal_code": "string", "city": "string", "country": "CH", "latitude": -90, "longitude": -180, "time_zone": "Europe/Zurich", "opening_hours": "string", "payment_integration_id": "string", "is_public": true } ``` Antwort 201: ```json { "id": "loc_2b8e1c0d9f7a6b5c", "object": "location", "name": "Hofladen", "street": "string", "postal_code": "string", "city": "string", "country": "string", "latitude": 0, "longitude": 0, "time_zone": "string", "opening_hours": "string", "payment_integration_id": "string", "is_public": true, "created_at": "string" } ``` ### Standort abrufen `GET /v1/locations/{id}` Ein Standort. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "loc_2b8e1c0d9f7a6b5c", "object": "location", "name": "Hofladen", "street": "string", "postal_code": "string", "city": "string", "country": "string", "latitude": 0, "longitude": 0, "time_zone": "string", "opening_hours": "string", "payment_integration_id": "string", "is_public": true, "created_at": "string" } ``` ### Standort ändern `PATCH /v1/locations/{id}` Ändert Name, Adresse, Öffnungszeiten oder das Zahlungskonto. Parameter: - `id` (path, string, Pflicht) Anfrage: ```json { "name": "string", "street": "string", "postal_code": "string", "city": "string", "country": "CH", "latitude": -90, "longitude": -180, "time_zone": "Europe/Zurich", "opening_hours": "string", "payment_integration_id": "string", "is_public": true } ``` Antwort 200: ```json { "id": "loc_2b8e1c0d9f7a6b5c", "object": "location", "name": "Hofladen", "street": "string", "postal_code": "string", "city": "string", "country": "string", "latitude": 0, "longitude": 0, "time_zone": "string", "opening_hours": "string", "payment_integration_id": "string", "is_public": true, "created_at": "string" } ``` ### Standort löschen `DELETE /v1/locations/{id}` Nur Standorte ohne Geräte. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "string", "object": "string", "deleted": true } ``` ## Geräte ### Geräte auflisten `GET /v1/devices` Ladepunkte mit aktuellem Zustand, Tarif, Abrechnungsmodell und QR-Code-URL. Parameter: - `location_id` (query, string) - `kind` (query, string) Antwort 200: ```json { "object": "list", "data": [ { "id": "dev_7d3a9c1b0e2f4a6b", "object": "device", "kind": "string", "livemode": true, "name": "Ladepunkt Scheune", "location_id": "string", "integration_id": "string", "manufacturer": "string", "model": "string", "serial_number": "string", "max_power_w": 0, "tariff_id": "string", "billing": {}, "enabled": true, "locked": true, "prepared": true, "status": "string", "state": { "online": true, "status": "string", "cable_plugged": true, "power_w": 0, "meter_wh": 0, "authorization_required": true, "last_seen_at": "2026-10-25T08:30:00.000Z", "error": "string" }, "active_session_id": "string", "connectors": [ { "id": "string", "code": "string", "url": "string" } ], "created_at": "string" } ], "has_more": true } ``` ### Gerät abrufen `GET /v1/devices/{id}` Ein Gerät mit Zustand. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "dev_7d3a9c1b0e2f4a6b", "object": "device", "kind": "string", "livemode": true, "name": "Ladepunkt Scheune", "location_id": "string", "integration_id": "string", "manufacturer": "string", "model": "string", "serial_number": "string", "max_power_w": 0, "tariff_id": "string", "billing": {}, "enabled": true, "locked": true, "prepared": true, "status": "string", "state": { "online": true, "status": "string", "cable_plugged": true, "power_w": 0, "meter_wh": 0, "authorization_required": true, "last_seen_at": "2026-10-25T08:30:00.000Z", "error": "string" }, "active_session_id": "string", "connectors": [ { "id": "string", "code": "string", "url": "string" } ], "created_at": "string" } ``` ### Gerät ändern `PATCH /v1/devices/{id}` Name, Standort, Tarif, Abrechnungsmodell (reservation, prepaid, both), Reservationsgrenzen, Pakete. `enabled: true` aktiviert das Gerät und setzt beim Hersteller «Autorisierung erforderlich». Parameter: - `id` (path, string, Pflicht) Anfrage: ```json { "name": "string", "location_id": "string", "tariff_id": "string", "billing": { "model": "reservation", "reservationDefault": 3000, "reservationMin": 1000, "reservationMax": 10000, "safetyMarginBps": 300, "packages": [], "prepaidRemainder": "refund", "plugTimeoutMinutes": 10, "maxDurationMinutes": 1440 }, "enabled": true } ``` Antwort 200: ```json { "id": "dev_7d3a9c1b0e2f4a6b", "object": "device", "kind": "string", "livemode": true, "name": "Ladepunkt Scheune", "location_id": "string", "integration_id": "string", "manufacturer": "string", "model": "string", "serial_number": "string", "max_power_w": 0, "tariff_id": "string", "billing": {}, "enabled": true, "locked": true, "prepared": true, "status": "string", "state": { "online": true, "status": "string", "cable_plugged": true, "power_w": 0, "meter_wh": 0, "authorization_required": true, "last_seen_at": "2026-10-25T08:30:00.000Z", "error": "string" }, "active_session_id": "string", "connectors": [ { "id": "string", "code": "string", "url": "string" } ], "created_at": "string" } ``` ### Zustand abfragen `GET /v1/devices/{id}/state` Aktueller Zustand. Mit `refresh=true` wird beim Hersteller nachgefragt. Parameter: - `id` (path, string, Pflicht) - `refresh` (query, boolean | null) Antwort 200: ```json { "online": true, "status": "string", "cable_plugged": true, "power_w": 0, "meter_wh": 0, "authorization_required": true, "last_seen_at": "2026-10-25T08:30:00.000Z", "error": "string" } ``` ### Ladung ohne Zahlung starten `POST /v1/devices/{id}/start` Gibt den Ladepunkt ohne Zahlung frei (z. B. eigenes Auto). Ladehase legt dafür eine Gratis-Sitzung an, damit die Energie erfasst wird. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 201: ```json { "id": "ses_1a2b3c4d5e6f7a8b9c0d", "object": "session", "livemode": true, "state": "string", "mode": "reservation", "channel": "qr", "location_id": "string", "device_id": "string", "connector_id": "string", "currency": "string", "authorized_amount": 1235, "amount": 1235, "captured_amount": 1235, "refunded_amount": 1235, "vat_amount": 1235, "energy_wh": 0, "idle_minutes": 0, "stop_reason": "string", "receipt_number": "string", "started_at": "2026-10-25T08:30:00.000Z", "ended_at": "2026-10-25T08:30:00.000Z", "settled_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Ladung am Gerät stoppen `POST /v1/devices/{id}/stop` Stoppt die laufende Sitzung des Geräts und rechnet ab. Ohne Sitzung wird nur der Stoppbefehl gesendet. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 200: ```json {} ``` ### Gerät sperren `POST /v1/devices/{id}/lock` Keine neuen Ladungen, laufende Ladung läuft weiter. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 200: ```json { "id": "dev_7d3a9c1b0e2f4a6b", "object": "device", "kind": "string", "livemode": true, "name": "Ladepunkt Scheune", "location_id": "string", "integration_id": "string", "manufacturer": "string", "model": "string", "serial_number": "string", "max_power_w": 0, "tariff_id": "string", "billing": {}, "enabled": true, "locked": true, "prepared": true, "status": "string", "state": { "online": true, "status": "string", "cable_plugged": true, "power_w": 0, "meter_wh": 0, "authorization_required": true, "last_seen_at": "2026-10-25T08:30:00.000Z", "error": "string" }, "active_session_id": "string", "connectors": [ { "id": "string", "code": "string", "url": "string" } ], "created_at": "string" } ``` ### Gerät entsperren `POST /v1/devices/{id}/unlock` Hebt die Sperre auf. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 200: ```json { "id": "dev_7d3a9c1b0e2f4a6b", "object": "device", "kind": "string", "livemode": true, "name": "Ladepunkt Scheune", "location_id": "string", "integration_id": "string", "manufacturer": "string", "model": "string", "serial_number": "string", "max_power_w": 0, "tariff_id": "string", "billing": {}, "enabled": true, "locked": true, "prepared": true, "status": "string", "state": { "online": true, "status": "string", "cable_plugged": true, "power_w": 0, "meter_wh": 0, "authorization_required": true, "last_seen_at": "2026-10-25T08:30:00.000Z", "error": "string" }, "active_session_id": "string", "connectors": [ { "id": "string", "code": "string", "url": "string" } ], "created_at": "string" } ``` ### Testgerät steuern `POST /v1/devices/{id}/simulate` Nur Testmodus: simuliertes Auto anstecken (`plug`), abziehen (`unplug`), offline, Störung, Leistung ändern. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "action": "plug", "vehicle_need_wh": 0, "power_w": 0 } ``` Antwort 200: ```json { "online": true, "status": "string", "cable_plugged": true, "power_w": 0, "meter_wh": 0, "authorization_required": true, "last_seen_at": "2026-10-25T08:30:00.000Z", "error": "string" } ``` ## Tarife ### Tarife auflisten `GET /v1/tariffs` Aktive Tarife mit aktueller Version. Antwort 200: ```json { "object": "list", "data": [ { "id": "trf_5e2a8b1c9d0f3e7a", "object": "tariff", "name": "string", "description": "string", "version": 0, "version_id": "string", "rules": {}, "archived": true, "created_at": "string", "updated_at": "string" } ], "has_more": true } ``` ### Tarif anlegen `POST /v1/tariffs` Tarifregeln: `energyPrice` in Hundertstel-Rappen pro kWh (4500 = 45 Rappen), `startFee` und `serviceFee` in Rappen, `timeWindows` für zeitabhängige Preise, `idleFee` für die Standzeitgebühr, `maxAmount` als Höchstbetrag. Preise inkl. MWST. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "name": "string", "description": "string", "rules": { "currency": "CHF", "energyPrice": 0, "startFee": 0, "serviceFee": 0, "timeWindows": [], "idleFee": { "pricePerMinute": 0, "graceMinutes": 0, "trigger": "after_charging", "maxAmount": 0, "quietHours": { "from": "string", "to": "string" } }, "maxAmount": 0, "timeZone": "Europe/Zurich" } } ``` Antwort 201: ```json { "id": "trf_5e2a8b1c9d0f3e7a", "object": "tariff", "name": "string", "description": "string", "version": 0, "version_id": "string", "rules": {}, "archived": true, "created_at": "string", "updated_at": "string" } ``` ### Tarif abrufen `GET /v1/tariffs/{id}` Ein Tarif mit Regeln der aktuellen Version. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "trf_5e2a8b1c9d0f3e7a", "object": "tariff", "name": "string", "description": "string", "version": 0, "version_id": "string", "rules": {}, "archived": true, "created_at": "string", "updated_at": "string" } ``` ### Tarif ändern `PATCH /v1/tariffs/{id}` Neue Regeln ergeben eine neue Version. Laufende Ladungen behalten ihre Version. Parameter: - `id` (path, string, Pflicht) Anfrage: ```json { "name": "string", "description": "string", "rules": { "currency": "CHF", "energyPrice": 0, "startFee": 0, "serviceFee": 0, "timeWindows": [], "idleFee": { "pricePerMinute": 0, "graceMinutes": 0, "trigger": "after_charging", "maxAmount": 0, "quietHours": { "from": "string", "to": "string" } }, "maxAmount": 0, "timeZone": "Europe/Zurich" } } ``` Antwort 200: ```json { "id": "trf_5e2a8b1c9d0f3e7a", "object": "tariff", "name": "string", "description": "string", "version": 0, "version_id": "string", "rules": {}, "archived": true, "created_at": "string", "updated_at": "string" } ``` ### Tarif archivieren `DELETE /v1/tariffs/{id}` Nur Tarife ohne aktive Geräte. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "trf_5e2a8b1c9d0f3e7a", "object": "tariff", "name": "string", "description": "string", "version": 0, "version_id": "string", "rules": {}, "archived": true, "created_at": "string", "updated_at": "string" } ``` ### Versionen auflisten `GET /v1/tariffs/{id}/versions` Alle Versionen eines Tarifs. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json {} ``` ### Tarifvorschau `POST /v1/tariffs/{id}/preview` Beispielbeträge, etwa «So viel kostet eine Ladung mit 20 kWh». Mit `rules` im Body lässt sich ein Entwurf berechnen. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "rules": { "currency": "CHF", "energyPrice": 0, "startFee": 0, "serviceFee": 0, "timeWindows": [], "idleFee": { "pricePerMinute": 0, "graceMinutes": 0, "trigger": "after_charging", "maxAmount": 0, "quietHours": { "from": "string", "to": "string" } }, "maxAmount": 0, "timeZone": "Europe/Zurich" }, "energy_wh": [ 10000, 20000, 40000 ], "power_w": 11000, "idle_minutes": 0, "start_at": "2026-10-25T08:30:00.000Z" } ``` Antwort 200: ```json {} ``` ### Vorschau für Entwurf `POST /v1/tariffs/preview` Wie die Tarifvorschau, aber ohne gespeicherten Tarif (Regeln im Body). Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "rules": { "currency": "CHF", "energyPrice": 0, "startFee": 0, "serviceFee": 0, "timeWindows": [], "idleFee": { "pricePerMinute": 0, "graceMinutes": 0, "trigger": "after_charging", "maxAmount": 0, "quietHours": { "from": "string", "to": "string" } }, "maxAmount": 0, "timeZone": "Europe/Zurich" }, "energy_wh": [ 10000, 20000, 40000 ], "power_w": 11000, "idle_minutes": 0, "start_at": "2026-10-25T08:30:00.000Z" } ``` Antwort 200: ```json {} ``` ## QR-Codes ### QR-Codes auflisten `GET /v1/codes` Statische QR-Codes mit URL und Anzahl Scans. Antwort 200: ```json { "object": "list", "data": [ { "id": "qr_3c9a1e7b0d2f4a6c", "object": "code", "code": "k7m2p9x", "target": "connector", "connector_id": "string", "location_id": "string", "label": "string", "url": "https://hof-meier.ladehase.com/k7m2p9x", "display_url": "string", "scans": 0, "disabled": true, "created_at": "string" } ], "has_more": true } ``` ### QR-Code anlegen `POST /v1/codes` Für einen Ladepunkt oder einen ganzen Standort (Auswahl auf der Bezahlseite). Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "target": "connector", "connector_id": "string", "location_id": "string", "label": "string" } ``` Antwort 201: ```json { "id": "qr_3c9a1e7b0d2f4a6c", "object": "code", "code": "k7m2p9x", "target": "connector", "connector_id": "string", "location_id": "string", "label": "string", "url": "https://hof-meier.ladehase.com/k7m2p9x", "display_url": "string", "scans": 0, "disabled": true, "created_at": "string" } ``` ### QR-Code abrufen `GET /v1/codes/{id}` Ein QR-Code. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "qr_3c9a1e7b0d2f4a6c", "object": "code", "code": "k7m2p9x", "target": "connector", "connector_id": "string", "location_id": "string", "label": "string", "url": "https://hof-meier.ladehase.com/k7m2p9x", "display_url": "string", "scans": 0, "disabled": true, "created_at": "string" } ``` ### QR-Code ändern `PATCH /v1/codes/{id}` Bezeichnung ändern oder deaktivieren. Parameter: - `id` (path, string, Pflicht) Anfrage: ```json { "label": "string", "disabled": true } ``` Antwort 200: ```json { "id": "qr_3c9a1e7b0d2f4a6c", "object": "code", "code": "k7m2p9x", "target": "connector", "connector_id": "string", "location_id": "string", "label": "string", "url": "https://hof-meier.ladehase.com/k7m2p9x", "display_url": "string", "scans": 0, "disabled": true, "created_at": "string" } ``` ### QR-Code als SVG `GET /v1/codes/{id}/qr.svg` Mit Bildmarke in der Mitte. Parameter: - `id` (path, string, Pflicht) ### QR-Code als PNG `GET /v1/codes/{id}/qr.png` 1024 × 1024 Pixel. Parameter: - `id` (path, string, Pflicht) ## Schilder ### Schilder erzeugen `POST /v1/signs/render` Druckfertiges PDF (mit Beschnitt und Schnittmarken) oder SVG-Vorschau. Formate: a6, a6_landscape, a5, a5_landscape, sticker_round_80, sticker_square_100, label_front. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "code_ids": [ "string" ], "format": "a6", "languages": [ "de", "fr", "it", "en" ], "show_tariff": true, "branding": true, "crop_marks": true, "output": "pdf" } ``` ### Schildprodukte `GET /v1/sign_products` Wetterfeste Schilder und Aufkleber zum Bestellen mit Preisen. Antwort 200: ```json {} ``` ### Schildbestellungen `GET /v1/sign_orders` Bestellungen der Organisation. Antwort 200: ```json {} ``` ### Schilder bestellen `POST /v1/sign_orders` Bestellung mit Zahlungslink. Wir drucken nach Zahlungseingang und senden per Post. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "items": [ { "product_id": "string", "code_id": "string", "quantity": 1, "languages": [ "de" ] } ], "shipping_address": { "name": "string", "street": "string", "postal_code": "string", "city": "string", "country": "CH" }, "note": "string" } ``` Antwort 201: ```json {} ``` ## Sitzungen ### Sitzungen auflisten `GET /v1/sessions` Ladungen, neueste zuerst. Filter: state (kommagetrennt), device_id, location_id, access_grant_id, from, to. Parameter: - `limit` (query, integer) - `starting_after` (query, string) - `state` (query, string) - `device_id` (query, string) - `location_id` (query, string) - `access_grant_id` (query, string) - `from` (query, string (date-time)) - `to` (query, string (date-time)) Antwort 200: ```json { "object": "list", "data": [ { "id": "ses_1a2b3c4d5e6f7a8b9c0d", "object": "session", "livemode": true, "state": "string", "mode": "reservation", "channel": "qr", "location_id": "string", "device_id": "string", "connector_id": "string", "currency": "string", "authorized_amount": 1235, "amount": 1235, "captured_amount": 1235, "refunded_amount": 1235, "vat_amount": 1235, "energy_wh": 0, "idle_minutes": 0, "stop_reason": "string", "receipt_number": "string", "started_at": "2026-10-25T08:30:00.000Z", "ended_at": "2026-10-25T08:30:00.000Z", "settled_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ], "has_more": true } ``` ### Sitzung starten `POST /v1/sessions` Startet eine Ladung ohne Zahlung über die API (z. B. für eigene Apps oder Weiterverrechnung). Mit `access_grant_id` gilt der Sondertarif des Zugangs. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "device_id": "string", "access_grant_id": "string", "metadata": {} } ``` Antwort 201: ```json { "id": "ses_1a2b3c4d5e6f7a8b9c0d", "object": "session", "livemode": true, "state": "string", "mode": "reservation", "channel": "qr", "location_id": "string", "device_id": "string", "connector_id": "string", "currency": "string", "authorized_amount": 1235, "amount": 1235, "captured_amount": 1235, "refunded_amount": 1235, "vat_amount": 1235, "energy_wh": 0, "idle_minutes": 0, "stop_reason": "string", "receipt_number": "string", "started_at": "2026-10-25T08:30:00.000Z", "ended_at": "2026-10-25T08:30:00.000Z", "settled_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Sitzung abrufen `GET /v1/sessions/{id}` Eine Ladung mit Beträgen, Energie und Preisaufstellung. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "ses_1a2b3c4d5e6f7a8b9c0d", "object": "session", "livemode": true, "state": "string", "mode": "reservation", "channel": "qr", "location_id": "string", "device_id": "string", "connector_id": "string", "currency": "string", "authorized_amount": 1235, "amount": 1235, "captured_amount": 1235, "refunded_amount": 1235, "vat_amount": 1235, "energy_wh": 0, "idle_minutes": 0, "stop_reason": "string", "receipt_number": "string", "started_at": "2026-10-25T08:30:00.000Z", "ended_at": "2026-10-25T08:30:00.000Z", "settled_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Sitzung stoppen `POST /v1/sessions/{id}/stop` Stoppt die Ladung. Abgerechnet wird automatisch. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 200: ```json { "id": "ses_1a2b3c4d5e6f7a8b9c0d", "object": "session", "livemode": true, "state": "string", "mode": "reservation", "channel": "qr", "location_id": "string", "device_id": "string", "connector_id": "string", "currency": "string", "authorized_amount": 1235, "amount": 1235, "captured_amount": 1235, "refunded_amount": 1235, "vat_amount": 1235, "energy_wh": 0, "idle_minutes": 0, "stop_reason": "string", "receipt_number": "string", "started_at": "2026-10-25T08:30:00.000Z", "ended_at": "2026-10-25T08:30:00.000Z", "settled_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Zeitleiste einer Sitzung `GET /v1/sessions/{id}/events` Alle Zustandswechsel und Messwerte (Ladekurve). Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json {} ``` ### Rückerstatten `POST /v1/sessions/{id}/refund` Voll oder teilweise (`amount` in Rappen). Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "amount": 0, "reason": "string" } ``` Antwort 200: ```json { "id": "ses_1a2b3c4d5e6f7a8b9c0d", "object": "session", "livemode": true, "state": "string", "mode": "reservation", "channel": "qr", "location_id": "string", "device_id": "string", "connector_id": "string", "currency": "string", "authorized_amount": 1235, "amount": 1235, "captured_amount": 1235, "refunded_amount": 1235, "vat_amount": 1235, "energy_wh": 0, "idle_minutes": 0, "stop_reason": "string", "receipt_number": "string", "started_at": "2026-10-25T08:30:00.000Z", "ended_at": "2026-10-25T08:30:00.000Z", "settled_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ## Zahlungen ### Zahlungen auflisten `GET /v1/payments` Spiegel der Zahlungen bei zahls.ch und Freika. Parameter: - `limit` (query, integer) - `starting_after` (query, string) - `status` (query, string) - `provider` (query, "zahls" | "freika" | "mock") Antwort 200: ```json { "object": "list", "data": [ { "id": "pay_8f1e2d3c4b5a6978", "object": "payment", "livemode": true, "session_id": "string", "provider": "zahls", "purpose": "string", "status": "string", "currency": "string", "amount": 1235, "captured_amount": 1235, "refunded_amount": 1235, "payment_method": "string", "card_masked": "string", "created_at": "string" } ], "has_more": true } ``` ### Zahlung abrufen `GET /v1/payments/{id}` Eine Zahlung. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "pay_8f1e2d3c4b5a6978", "object": "payment", "livemode": true, "session_id": "string", "provider": "zahls", "purpose": "string", "status": "string", "currency": "string", "amount": 1235, "captured_amount": 1235, "refunded_amount": 1235, "payment_method": "string", "card_masked": "string", "created_at": "string" } ``` ## Terminals ### Terminals auflisten `GET /v1/terminals` Gekoppelte PAX IM30. Antwort 200: ```json { "object": "list", "data": [ { "id": "trm_6b0c2d8e1f3a5b7c", "object": "terminal", "name": "string", "model": "string", "location_id": "string", "integration_id": "string", "external_terminal_id": "string", "paired": true, "online": true, "app_version": "string", "last_seen_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ], "has_more": true } ``` ### Terminal anlegen `POST /v1/terminals` Neues PAX IM30 für einen Standort. Antwort enthält den Wartungs-PIN (nur einmal sichtbar). Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "name": "PAX IM30", "location_id": "string", "integration_id": "string", "external_terminal_id": "string", "maintenance_pin": "string" } ``` Antwort 201: ```json { "id": "trm_6b0c2d8e1f3a5b7c", "object": "terminal", "name": "string", "model": "string", "location_id": "string", "integration_id": "string", "external_terminal_id": "string", "paired": true, "online": true, "app_version": "string", "last_seen_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Freika-Terminals `GET /v1/terminals/available` Terminals im verbundenen Freika-Konto zur Auswahl beim Koppeln. Antwort 200: ```json {} ``` ### Terminal ändern `PATCH /v1/terminals/{id}` Name, Standort, Freika-Terminal, Wartungs-PIN. Parameter: - `id` (path, string, Pflicht) Anfrage: ```json { "name": "PAX IM30", "location_id": "string", "integration_id": "string", "external_terminal_id": "string", "maintenance_pin": "string" } ``` Antwort 200: ```json { "id": "trm_6b0c2d8e1f3a5b7c", "object": "terminal", "name": "string", "model": "string", "location_id": "string", "integration_id": "string", "external_terminal_id": "string", "paired": true, "online": true, "app_version": "string", "last_seen_at": "2026-10-25T08:30:00.000Z", "created_at": "string" } ``` ### Terminal entfernen `DELETE /v1/terminals/{id}` Die App muss danach neu gekoppelt werden. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "string", "object": "string", "deleted": true } ``` ### Kopplungscode `POST /v1/terminals/{id}/pairing_codes` Code und QR-Inhalt zum Koppeln der Terminal-App (15 Minuten gültig). Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 201: ```json {} ``` ## Zugänge ### Zugänge auflisten `GET /v1/access_grants` Personen und Ladekarten, die gratis oder zum Sondertarif laden. Antwort 200: ```json { "object": "list", "data": [ { "id": "acc_0e9d8c7b6a5f4e3d", "object": "access_grant", "name": "string", "email": "string", "rfid_uid": "string", "link_url": "string", "free": true, "tariff_id": "string", "monthly_limit_wh": 0, "revoked": true, "created_at": "string" } ], "has_more": true } ``` ### Zugang anlegen `POST /v1/access_grants` Ohne `tariff_id` gratis, mit `tariff_id` Sondertarif und Monatsabrechnung. Erzeugt einen persönlichen Link. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "name": "string", "email": "person@example.ch", "rfid_uid": "string", "create_link": true, "valid_from": "2026-10-25T08:30:00.000Z", "valid_until": "2026-10-25T08:30:00.000Z", "device_ids": [], "tariff_id": "string", "monthly_limit_wh": 0, "billing_integration_id": "string", "note": "string" } ``` Antwort 201: ```json { "id": "acc_0e9d8c7b6a5f4e3d", "object": "access_grant", "name": "string", "email": "string", "rfid_uid": "string", "link_url": "string", "free": true, "tariff_id": "string", "monthly_limit_wh": 0, "revoked": true, "created_at": "string" } ``` ### Zugang abrufen `GET /v1/access_grants/{id}` Ein Zugang. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "acc_0e9d8c7b6a5f4e3d", "object": "access_grant", "name": "string", "email": "string", "rfid_uid": "string", "link_url": "string", "free": true, "tariff_id": "string", "monthly_limit_wh": 0, "revoked": true, "created_at": "string" } ``` ### Zugang ändern `PATCH /v1/access_grants/{id}` Auch `revoked` und `regenerate_link`. Parameter: - `id` (path, string, Pflicht) Anfrage: ```json { "name": "string", "email": "person@example.ch", "rfid_uid": "string", "create_link": true, "valid_from": "2026-10-25T08:30:00.000Z", "valid_until": "2026-10-25T08:30:00.000Z", "device_ids": [], "tariff_id": "string", "monthly_limit_wh": 0, "billing_integration_id": "string", "note": "string", "revoked": true, "regenerate_link": true } ``` Antwort 200: ```json { "id": "acc_0e9d8c7b6a5f4e3d", "object": "access_grant", "name": "string", "email": "string", "rfid_uid": "string", "link_url": "string", "free": true, "tariff_id": "string", "monthly_limit_wh": 0, "revoked": true, "created_at": "string" } ``` ### Zugang widerrufen `DELETE /v1/access_grants/{id}` Link und Berechtigung werden ungültig. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "string", "object": "string", "deleted": true } ``` ### Monatsabrechnungen `GET /v1/billing_statements` Abrechnungen für Zugänge mit Sondertarif. Parameter: - `period` (query, string) Antwort 200: ```json {} ``` ### Abrechnungen erstellen `POST /v1/billing_statements/generate` Für einen Monat (`period` = JJJJ-MM). Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "period": "string" } ``` Antwort 200: ```json {} ``` ### Abrechnung senden `POST /v1/billing_statements/{id}/send` E-Mail mit Zahlungslink über zahls.ch oder Freika. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 200: ```json {} ``` ### Als bezahlt markieren `POST /v1/billing_statements/{id}/paid` Zum Beispiel bei Barzahlung. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 200: ```json {} ``` ## Berichte ### Kennzahlen `GET /v1/reports/summary` Ladungen, Energie, Umsatz, MWST, Tagesreihe, pro Gerät und Kanal (Standard: letzte 30 Tage). Parameter: - `from` (query, string (date-time)) - `to` (query, string (date-time)) - `location_id` (query, string) - `device_id` (query, string) - `access_grant_id` (query, string) Antwort 200: ```json {} ``` ### Export `GET /v1/reports/export` CSV, XLSX oder PDF für Buchhaltung und Weiterverrechnung. Parameter: - `from` (query, string (date-time)) - `to` (query, string (date-time)) - `location_id` (query, string) - `device_id` (query, string) - `access_grant_id` (query, string) - `format` (query, "csv" | "xlsx" | "pdf") ## Team ### Mitglieder `GET /v1/members` Mitglieder und offene Einladungen. Antwort 200: ```json {} ``` ### Person einladen `POST /v1/invitations` Rollen: owner, admin, member. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "email": "person@example.ch", "role": "member" } ``` Antwort 201: ```json {} ``` ### Einladung zurückziehen `DELETE /v1/invitations/{id}` Der Link wird ungültig. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "string", "object": "string", "deleted": true } ``` ### Rolle ändern `PATCH /v1/members/{id}` Mindestens eine Inhaberin oder ein Inhaber bleibt. Parameter: - `id` (path, string, Pflicht) Anfrage: ```json { "role": "owner" } ``` Antwort 200: ```json {} ``` ### Mitglied entfernen `DELETE /v1/members/{id}` Entfernt die Person aus der Organisation. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "string", "object": "string", "deleted": true } ``` ## Webhooks ### Webhook-Endpunkte `GET /v1/webhook_endpoints` Empfänger für Ereignisse. Antwort 200: ```json { "object": "list", "data": [ { "id": "whk_2f4a6c8e0b1d3f5a", "object": "webhook_endpoint", "livemode": true, "url": "string", "description": "string", "events": [ "string" ], "enabled": true, "secret": "string", "created_at": "string" } ], "has_more": true } ``` ### Webhook anlegen `POST /v1/webhook_endpoints` Signatur `Ladehase-Signature: t=…,v1=…` (HMAC-SHA256 über `t.body`). Das Geheimnis ist nur in dieser Antwort sichtbar. Parameter: - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Anfrage: ```json { "url": "https://example.ch/webhooks/ladehase", "description": "string", "events": [ "*" ], "enabled": true } ``` Antwort 201: ```json { "id": "whk_2f4a6c8e0b1d3f5a", "object": "webhook_endpoint", "livemode": true, "url": "string", "description": "string", "events": [ "string" ], "enabled": true, "secret": "string", "created_at": "string" } ``` ### Webhook ändern `PATCH /v1/webhook_endpoints/{id}` Auch `rotate_secret`. Parameter: - `id` (path, string, Pflicht) Anfrage: ```json { "url": "https://example.ch/webhooks/ladehase", "description": "string", "events": [ "*" ], "enabled": true, "rotate_secret": true } ``` Antwort 200: ```json { "id": "whk_2f4a6c8e0b1d3f5a", "object": "webhook_endpoint", "livemode": true, "url": "string", "description": "string", "events": [ "string" ], "enabled": true, "secret": "string", "created_at": "string" } ``` ### Webhook löschen `DELETE /v1/webhook_endpoints/{id}` Keine weiteren Zustellungen. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json { "id": "string", "object": "string", "deleted": true } ``` ### Zustellprotokoll `GET /v1/webhook_endpoints/{id}/deliveries` Letzte 100 Zustellungen mit Antwortcode. Parameter: - `id` (path, string, Pflicht) Antwort 200: ```json {} ``` ### Ereignisse `GET /v1/events` Alle Ereignisse, neueste zuerst. Parameter: - `limit` (query, integer) - `starting_after` (query, string) - `type` (query, string) Antwort 200: ```json { "object": "list", "data": [ { "id": "evt_1b3d5f7a9c0e2f4b", "object": "event", "type": "string", "livemode": true, "data": {}, "created_at": "string" } ], "has_more": true } ``` ### Ereignis erneut senden `POST /v1/events/{id}/resend` An alle passenden Endpunkte. Parameter: - `id` (path, string, Pflicht) - `idempotency-key` (header, string): Wiederholbare Anfragen ohne doppelte Wirkung (24 Stunden) Antwort 200: ```json {} ```