# 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
{}
```
