# Lebenszyklus-Ereignisse erfassen

> Melden Sie Inspektionen, Reparaturen und andere Ereignisse im Leben einer Einheit mit createLifecycleEvent und lesen Sie den Lebenszyklus mit listLifecycleEvents.

Ein Lebenszyklus-Ereignis hält fest, dass mit einer Batterie etwas geschehen ist: Sie wurde in Betrieb genommen,
geprüft, repariert, ein Bauteil wurde getauscht. Ereignisse sind Aufzeichnungen und ändern keine Daten der
Einheit. Messwerte melden Sie mit [`updateCondition`](https://passport.kabin.sh/docs/de/guides/push-condition-data). Hintergrund:
[Zustand und Lebenszyklus](https://passport.kabin.sh/docs/de/concepts/condition-and-lifecycle).

| Operation                                                                                                    | Scope       | Was sie tut                                  |
| ------------------------------------------------------------------------------------------------------------ | ----------- | -------------------------------------------- |
| [`createLifecycleEvent`](https://passport.kabin.sh/docs/de/api-reference/lifecycle/create-lifecycle-event) | `condition` | Erfasst ein Ereignis für eine Einheit        |
| [`listLifecycleEvents`](https://passport.kabin.sh/docs/de/api-reference/lifecycle/list-lifecycle-events)   | `read`      | Der Lebenszyklus der Einheit, neueste zuerst |

## Ereignisarten

| `type`               | Wofür                                                           |
| -------------------- | --------------------------------------------------------------- |
| `placed_on_market`   | Die Batterie wurde in Verkehr gebracht.                         |
| `put_into_service`   | Die Batterie wurde eingebaut und in Betrieb genommen.           |
| `inspection`         | Eine Prüfung oder ein Wartungstermin.                           |
| `repair`             | Eine Reparatur.                                                 |
| `component_replaced` | Ein Modul, eine Zelle oder ein anderes Bauteil wurde getauscht. |
| `waste`              | Die Batterie ist zu Abfall geworden.                            |
| `recycled`           | Die Batterie wurde dem Recycling übergeben.                     |
| `custom`             | Alles andere; beschreiben Sie es in `description`.              |

`ReportedEventType` im Vertrag nennt zusätzlich `condition_update`, doch `createLifecycleEvent` lehnt diesen Typ
mit `400 validation_error` auf `type` ab: Solche Ereignisse schreibt `updateCondition`. Die Arten des zweiten
Lebens (`repurposed`, `reused`, `remanufactured`) erscheinen im Lebenszyklus, wenn ein Mitglied im Portal eine
Einheit für das zweite Leben anlegt; über die API lassen sie sich nicht erfassen.

## Ein Ereignis erfassen

1. **Dokumente finden (optional)**

   `evidence_ids` nennt Dokumente aus der Nachweisbibliothek Ihrer Organisation, etwa einen Diagnosebericht oder ein
   Reparaturprotokoll, höchstens 20 und jedes nur einmal. Mitglieder laden Dokumente im Portal hoch; die IDs finden
   Sie mit [`listEvidence`](https://passport.kabin.sh/docs/de/api-reference/evidence/list-evidence) (Schlüssel mit Scope `read`), zum
   Beispiel mit `?kind=diagnostic_report`.

2. **Ereignis senden**

   `POST /api/v1/batteries/{batteryId}/lifecycle-events`

   - **type** `string` (body, erforderlich): Eine der Arten oben.
   - **occurred_on** `date` (body, erforderlich): Der Tag des Ereignisses (`YYYY-MM-DD`), zwischen dem Herstellungsdatum der Einheit und heute.
   - **description** `string` (body): Höchstens 2000 Zeichen.
   - **evidence_ids** `uuid[]` (body): Höchstens 20 verschiedene Dokumente der Nachweisbibliothek.

   ```bash title="cURL"
   curl -sS -X POST "$BATTERYPASS_URL/api/v1/batteries/0192a3f0-5b7c-7d21-8e4f-6a2b9c1d3e5f/lifecycle-events" \
     -H "Authorization: Bearer $BATTERYPASS_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "type": "inspection",
       "occurred_on": "2026-10-22",
       "description": "Jahresinspektion: Gehäuse, Steckverbinder und BMS-Protokoll geprüft, ohne Befund.",
       "evidence_ids": ["0192f7aa-1c2d-7e3f-8a4b-5c6d7e8f9a0b"]
     }'
   ```

   ```ts title="TypeScript"
   const batteryId = '0192a3f0-5b7c-7d21-8e4f-6a2b9c1d3e5f';
   const res = await fetch(`${process.env.BATTERYPASS_URL}/api/v1/batteries/${batteryId}/lifecycle-events`, {
     method: 'POST',
     headers: {
       Authorization: `Bearer ${process.env.BATTERYPASS_API_KEY}`,
       'Content-Type': 'application/json',
     },
     body: JSON.stringify({
       type: 'inspection',
       occurred_on: '2026-10-22',
       description: 'Jahresinspektion: Gehäuse, Steckverbinder und BMS-Protokoll geprüft, ohne Befund.',
       evidence_ids: ['0192f7aa-1c2d-7e3f-8a4b-5c6d7e8f9a0b'],
     }),
   });
   const event = await res.json();
   if (res.status !== 201) throw new Error(`${res.status} ${event.code} (request_id ${event.request_id})`);
   console.log(event.id, event.type, event.occurred_on);
   ```

   ```python title="Python"
   import os

   import requests

   battery_id = "0192a3f0-5b7c-7d21-8e4f-6a2b9c1d3e5f"
   res = requests.post(
       f"{os.environ['BATTERYPASS_URL']}/api/v1/batteries/{battery_id}/lifecycle-events",
       headers={"Authorization": f"Bearer {os.environ['BATTERYPASS_API_KEY']}"},
       json={
           "type": "inspection",
           "occurred_on": "2026-10-22",
           "description": "Jahresinspektion: Gehäuse, Steckverbinder und BMS-Protokoll geprüft, ohne Befund.",
           "evidence_ids": ["0192f7aa-1c2d-7e3f-8a4b-5c6d7e8f9a0b"],
       },
       timeout=30,
   )
   event = res.json()
   if res.status_code != 201:
       raise SystemExit(f"{res.status_code} {event['code']} (request_id {event.get('request_id')})")
   print(event["id"], event["type"], event["occurred_on"])
   ```

   ```go title="Go"
   package main

   import (
   	"bytes"
   	"encoding/json"
   	"fmt"
   	"log"
   	"net/http"
   	"os"
   )

   func main() {
   	batteryID := "0192a3f0-5b7c-7d21-8e4f-6a2b9c1d3e5f"
   	body, _ := json.Marshal(map[string]any{
   		"type":         "inspection",
   		"occurred_on":  "2026-10-22",
   		"description":  "Jahresinspektion: Gehäuse, Steckverbinder und BMS-Protokoll geprüft, ohne Befund.",
   		"evidence_ids": []string{"0192f7aa-1c2d-7e3f-8a4b-5c6d7e8f9a0b"},
   	})
   	url := os.Getenv("BATTERYPASS_URL") + "/api/v1/batteries/" + batteryID + "/lifecycle-events"
   	req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
   	req.Header.Set("Authorization", "Bearer "+os.Getenv("BATTERYPASS_API_KEY"))
   	req.Header.Set("Content-Type", "application/json")
   	res, err := http.DefaultClient.Do(req)
   	if err != nil {
   		log.Fatal(err) // do not resend blindly: check listLifecycleEvents first
   	}
   	defer res.Body.Close()
   	var event map[string]any
   	if err := json.NewDecoder(res.Body).Decode(&event); err != nil {
   		log.Fatal(err)
   	}
   	if res.StatusCode != http.StatusCreated {
   		log.Fatalf("%d %v (request_id %v)", res.StatusCode, event["code"], event["request_id"])
   	}
   	fmt.Println(event["id"], event["type"], event["occurred_on"])
   }
   ```

   ```json title="201"
   {
     "id": "0192f8c2-4d5e-7f60-8a71-b2c3d4e5f6a7",
     "battery_id": "0192a3f0-5b7c-7d21-8e4f-6a2b9c1d3e5f",
     "type": "inspection",
     "occurred_on": "2026-10-22",
     "description": "Jahresinspektion: Gehäuse, Steckverbinder und BMS-Protokoll geprüft, ohne Befund.",
     "source": "api",
     "api_key": { "id": "0192e4c7-8b9a-7c6d-8e5f-4a3b2c1d0e9f", "name": "Servicesystem Nord" },
     "field_keys": [],
     "evidence": [
       {
         "id": "0192f7aa-1c2d-7e3f-8a4b-5c6d7e8f9a0b",
         "kind": "diagnostic_report",
         "title": "Prüfprotokoll 2026",
         "filename": "pruefprotokoll-2026-10-22.pdf",
         "access_tier": "internal",
         "revision": 1,
         "superseded": false
       }
     ],
     "created_at": "2026-10-24T08:15:02Z"
   }
   ```

   Ein Ereignis, das mit einem Schlüssel gesendet wurde, hat `source: api` und nennt den Schlüssel in `api_key`;
   Ereignisse von Mitgliedern tragen `reported_by`, solche von Servicepartnern `partner`.

## Keine automatische Duplikaterkennung

BatteryPass erkennt nicht, wenn dasselbe Ereignis zweimal ankommt: Jede erfolgreiche Anfrage legt ein Ereignis an,
und `createLifecycleEvent` kennt keinen Idempotenzschlüssel. Das ist wichtig, wenn eine Anfrage in ein Timeout
läuft und Sie nicht wissen, ob sie angekommen ist:

- Wiederholen Sie einen `POST` nach einem Netzwerkfehler, einem Timeout oder einem `5xx` nicht automatisch.
- Bevor Sie es erneut senden, suchen Sie Ihr Ereignis (gleicher `type`, gleiches `occurred_on` und gleiche
  `description`, `source: api`) in `listLifecycleEvents`. Die Liste ist nach `occurred_on` sortiert, das neueste
  zuerst; ein rückdatiertes Ereignis steht also nicht unbedingt auf der ersten Seite. Lesen Sie mit `next_cursor`
  weiter, bis die Seite Ereignisse enthält, die vor Ihrem lagen (das
  [Beispiel für Wiederholungen](https://passport.kabin.sh/docs/de/idempotency) macht genau das).
- Speichern Sie die zurückgegebene `id` in Ihrem System, damit Sie wissen, welche Ereignisse bereits erfasst sind.

Ein `429 rate_limited` dürfen Sie nach `Retry-After` gefahrlos wiederholen: Die Anfrage wurde abgelehnt, bevor
etwas gespeichert wurde. Mehr dazu unter [Wiederholungen und Idempotenz](https://passport.kabin.sh/docs/de/idempotency).

## Den Lebenszyklus lesen

```bash title="cURL"
curl -sS "$BATTERYPASS_URL/api/v1/batteries/0192a3f0-5b7c-7d21-8e4f-6a2b9c1d3e5f/lifecycle-events?limit=50" \
  -H "Authorization: Bearer $BATTERYPASS_API_KEY"
```

Die Antwort ist eine Seite `{items, next_cursor}`, das jüngste `occurred_on` zuerst; ältere Ereignisse folgen über
`next_cursor` ([Paginierung](https://passport.kabin.sh/docs/de/pagination)). Die Liste enthält jedes Ereignis der Einheit, gleich wer es
gemeldet hat: Mitglieder, Servicepartner über ihre Links, Zustandsupdates (`type: condition_update` mit den
aktualisierten `field_keys`), Schlüssel und das System. Beim zweiten Leben nennen `related_battery_id` und
`related_serial_number` die jeweils andere Einheit.

## Fehler

| Status und `code`      | Typische Ursache                                                                                                                                                                                                                           |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `400 validation_error` | `type` nicht erlaubt (auch `condition_update`); `occurred_on` fehlt, liegt in der Zukunft oder vor dem Herstellungsdatum; `description` zu lang; ein Eintrag in `evidence_ids` ist kein Dokument Ihrer Organisation oder kommt doppelt vor |
| `401 unauthenticated`  | Schlüssel fehlt, ist unbekannt oder widerrufen                                                                                                                                                                                             |
| `403 forbidden`        | Dem Schlüssel fehlt der Scope `condition` (für `listLifecycleEvents`: `read`)                                                                                                                                                              |
| `404 not_found`        | Die Einheit gibt es in der Organisation des Schlüssels nicht                                                                                                                                                                               |
| `409 conflict`         | Das Batteriemodell der Einheit ist archiviert                                                                                                                                                                                              |
| `429 rate_limited`     | Zu viele Anfragen; `Retry-After` abwarten                                                                                                                                                                                                  |
