# Quickstart — 10 Minuten zur ersten Integration

Ziel: API-Key erstellen, erstes Sample anlegen, Outbound-Webhook empfangen.

## 1 — API-Key erstellen (1 min)

1. In LabNote einloggen → **Einstellungen → API**.
2. **Neuer Key** → Name, Scopes auswählen (`read`, `write`).
3. Key kopieren — er wird nur einmal angezeigt.

```bash
export LABNOTE_KEY="lk_live_..."
export LABNOTE_BASE="https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/api-v1"
```

## 2 — Verbindung testen (30 s)

```bash
curl -sS "$LABNOTE_BASE/v1/ping" \
  -H "Authorization: Bearer $LABNOTE_KEY"
```

Antwort:
```json
{
  "ok": true,
  "message": "LabNote-Light API v1",
  "authenticated_as": { "kind": "api_key", "user_id": "…", "org_id": "…" },
  "resources": ["experiments", "projects", "samples", "equipment", "reagents",
                "stock-items", "stock-movements", "inventories",
                "audit", "jobs", "notifications"]
}
```

`GET /v1/health` beantwortet dieselbe Frage ohne Token (Monitoring).

## 3 — Sample anlegen (1 min)

```bash
curl -sS -X POST "$LABNOTE_BASE/v1/samples" \
  -H "Authorization: Bearer $LABNOTE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "S-test-001",
    "type": "liquid",
    "status": "active"
  }'
```

## 4 — Outbound-Webhook abonnieren (3 min)

1. **Einstellungen → Integrationen → Outbound Webhooks → Neu**.
2. URL (z. B. `https://webhook.site/<your-id>` zum Testen).
3. Events: `sample.*`, `experiment.signed`.
4. `signing_secret` kopieren.

Trigger durch ein UPDATE auf das Sample aus Schritt 3 → der Webhook trifft binnen
Sekunden ein. Verifikation:

```ts
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody: string, header: string, secret: string) {
  const [tPart, sigPart] = header.split(",");
  const ts  = tPart.split("=")[1];
  const got = sigPart.split("=")[1];
  if (Math.abs(Date.now()/1000 - Number(ts)) > 300) return false;

  const expected = createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`).digest("hex");

  return timingSafeEqual(Buffer.from(got, "hex"), Buffer.from(expected, "hex"));
}
```

## 5 — Inbound-Webhook senden (3 min)

Optional, wenn das Fremdsystem Daten in LabNote pushen soll.
Vollständig dokumentiert in [inbound-webhooks.md](./inbound-webhooks.md).

## Endpunkt-Übersicht

Basis-URL: `$LABNOTE_BASE` — alle Pfade beginnen mit `/v1`.
Schreibende Requests akzeptieren `Idempotency-Key`. Rate-Limit: 120 req/min, 5000 req/h pro Token.

| Endpunkt | Methoden | Scope | Zweck |
| --- | --- | --- | --- |
| `/v1/health` | GET | — | Liveness ohne Auth |
| `/v1/ping` | GET | read | Identität, Org und Ressourcen-Registry |
| `/v1/experiments`, `/v1/experiments/{id}` | GET, POST, PATCH, DELETE | read/write | Experimente (ELN-Einträge) |
| `/v1/projects`, `/v1/projects/{id}` | GET, POST, PATCH, DELETE | read/write | Projekte (abteilungsgescoped) |
| `/v1/samples`, `/v1/samples/{id}` | GET, POST, PATCH, DELETE | read/write | Proben |
| `/v1/equipment`, `/v1/equipment/{id}` | GET, POST, PATCH, DELETE | read/write | Geräte-Stammdaten |
| `/v1/equipment/{id}/usage` | GET, POST | read/write | Nutzungssitzungen |
| `/v1/equipment/{id}/maintenance` | GET, POST | read/write | Wartungslogs |
| `/v1/equipment/{id}/calibrations` | GET, POST | read/write | Kalibrierungen |
| `/v1/equipment/{id}/qualifications` | GET, POST | read/write | IQ/OQ/PQ |
| `/v1/equipment/{id}/{sub}/{subId}` | PATCH, PUT | write | Einzelnen Sub-Datensatz ändern |
| `/v1/equipment/{id}/remote` | GET, POST, PATCH, DELETE | read/write | Remote-Zugriff (schaltet den Button im Gerätedetail frei) |
| `/v1/reagents`, `/v1/reagents/{id}` | GET, POST, PATCH, DELETE | read/write | Reagenzien-Katalog |
| `/v1/stock-items`, `/v1/stock-items/{id}` | GET, POST, PATCH, DELETE | read/write | Gebinde/Bestand |
| `/v1/stock-movements` | GET, POST | read/write | Bestandsbewegungen (append-only) |
| `/v1/inventories`, `/v1/inventories/{id}` | GET, POST, PATCH, DELETE | read/write | Inventuren |
| `/v1/devices`, `/v1/devices/{id}` | GET, POST, DELETE | read/write | Messgeräte-Registrierung (Upsert über `external_device_id`) |
| `/v1/results` | GET, POST | read/write | Messergebnisse (JSON-Ingest, Vendor-Adapter) |
| `/v1/results/upload` | POST | write | Rohdatei-Upload (CSV, JCAMP-DX, mzML; 25 MB / 50 MB mzML) |
| `/v1/results/{id}` | GET | read | Einzelnes Ergebnis inkl. Gerätedaten |
| `/v1/results/{id}/chart` | GET | read | Chart-Daten und erkannte Peaks |
| `/v1/notifications` | GET, POST | read/write | Eigene Benachrichtigungen lesen, an User/Rollen pushen |
| `/v1/audit` | GET | read | Audit-Trail-Export (`from`, `to`, `limit`) |
| `/v1/jobs`, `/v1/jobs/{id}` | GET, POST | read/write | Hintergrund-Jobs einreihen und pollen |

Weitere Edge-Funktionen (nicht Teil von `/v1`): `/org-data-export`, `/gdpr-delete-account`,
`/send-transactional-email`, `/oauth-authorize`, `/oauth-token`, `/handle-email-unsubscribe`,
`/rpc/search_experiments`, `/rpc/create_signature` — dokumentiert in [openapi.yaml](./openapi.yaml).

## Nächste Schritte

- Vollständige Endpunkt-Referenz: [openapi.yaml](./openapi.yaml)
- Event-Katalog: [events.md](./events.md)
- Fehler-Codes: [errors.md](./errors.md)
- Postman-Collection: [labnote-api.postman_collection.json](./labnote-api.postman_collection.json)
