GET /v1/healthDienststatus ohne Token; keine Prüfung der Integrationsberechtigungen.
Cookies und ähnliche Technologien
Wir verwenden technisch notwendige Cookies, um die Funktionalität der Plattform sicherzustellen. Optional können wir mit deiner Einwilligung Analyse-Cookies setzen. Du kannst deine Auswahl jederzeit ändern. Mehr in der Datenschutzerklärung.
Entwickler-Dokumentation · Oktober 2026
REST-Schnittstellen, Geräteanbindung und Webhooks: Endpunkte, Berechtigungen und Beispiele für die technische Integration. MCP und OAuth sind getrennt beschrieben.
REST base URL
https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/api-v1REST akzeptiert Organisations-API-Keys und persönliche Access Tokens (PAT) als Bearer-Token, nicht beliebige Anmeldetokens. API-Keys sind an eine Organisation gebunden. Ungebundene PATs benötigen X-Org-Id oder ?org_id=<uuid>; bei gebundenen Tokens darf die angegebene Organisation nicht abweichen. PAT-Mitgliedschaften werden bei jeder Anfrage geprüft. Tokens nur serverseitig speichern und nicht in Browsercode oder öffentliche Repositories übernehmen.
read erlaubt reguläre Lesezugriffe; write schließt read ein. Der Audit-Endpunkt benötigt audit:read. Scopes ersetzen keine Rollen-, Abteilungs- oder Datensatzberechtigungen. Löschungen generischer Datensätze erfordern eine Administratorrolle. Geräte-, Messdaten- und Auftragsendpunkte setzen das zugehörige aktivierte Modul voraus. Signaturen werden nicht über generische REST-Schreibzugriffe gesetzt.
export LABNOTE_API="https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/api-v1"
# LABNOTE_API_KEY: set securely in your environment
curl -sS "$LABNOTE_API/v1/ping" \
-H "Authorization: Bearer $LABNOTE_API_KEY"
curl -sS "$LABNOTE_API/v1/experiments?limit=20" \
-H "Authorization: Bearer $LABNOTE_PAT" \
-H "X-Org-Id: <org-uuid>"Die Pfade werden an die REST-Basis-URL angehängt. Methoden gelten für den jeweils genannten Listen- oder Einzelpfad; zulässige Felder und zusätzliche Berechtigungen unterscheiden sich je Ressource. Die Referenz beschreibt den vorhandenen Code, keine pauschale Freigabe für jede Organisation.
GET /v1/healthDienststatus ohne Token; keine Prüfung der Integrationsberechtigungen.
GET /v1/pingTokenidentität, Organisation und Ressourcenliste.
GET POST /v1/experiments
GET PATCH PUT DELETE /v1/experiments/{id}Generisches CRUD; erlaubte Felder und datensatzabhängige Rechte.
GET POST /v1/projects
GET PATCH PUT DELETE /v1/projects/{id}Generisches CRUD; erlaubte Felder und datensatzabhängige Rechte.
GET POST /v1/samples
GET PATCH PUT DELETE /v1/samples/{id}Generisches CRUD; erlaubte Felder und datensatzabhängige Rechte.
GET POST /v1/equipment
GET PATCH PUT DELETE /v1/equipment/{id}Generisches CRUD; erlaubte Felder und datensatzabhängige Rechte. Benötigt lab_equipment.
GET POST /v1/reagents
GET PATCH PUT DELETE /v1/reagents/{id}Generisches CRUD; erlaubte Felder und datensatzabhängige Rechte.
GET POST /v1/stock-items
GET PATCH PUT DELETE /v1/stock-items/{id}Generisches CRUD; erlaubte Felder und datensatzabhängige Rechte.
GET POST /v1/inventories
GET PATCH PUT DELETE /v1/inventories/{id}Generisches CRUD; erlaubte Felder und datensatzabhängige Rechte.
GET POST /v1/stock-movements
GET DELETE /v1/stock-movements/{id}Bewegungen lesen und anlegen; keine aktualisierbaren Felder.
GET POST /v1/equipment/{id}/{sub}
PATCH PUT /v1/equipment/{id}/{sub}/{subId}sub: usage, maintenance, calibrations oder qualifications. Datensätze des zugänglichen Geräts.
GET POST PUT PATCH DELETE
/v1/equipment/{id}/remoteRemote-Link; POST/PUT Upsert, PATCH vorhandener Link, DELETE 204. Kein Remote-Steuerungsprotokoll.
GET POST /v1/devices
DELETE /v1/devices/{id}Messgeräte registrieren und auflisten; Löschen nur als Administrator. Kein GET-Einzelpfad.
GET POST /v1/results
GET /v1/results/{id}
GET /v1/results/{id}/chart
POST /v1/results/uploadMessdaten-Ingest, Rohdatei-Upload und Ergebnisdaten; analytics_qc-Modul erforderlich.
GET POST /v1/connectors
GET DELETE /v1/connectors/{id}Connector-Heartbeat und Geräteabgleich; Löschen nur als Administrator.
GET /v1/auditAudit-Trail mit audit:read und zweiteiligem Cursor.
GET POST /v1/jobs
GET /v1/jobs/{id}Zugelassene Hintergrundjobs einreichen und Status lesen.
GET POST /v1/notificationsNutzerbezogen lesen; mit write an Organisationsmitglieder senden.
GET /v1/environmental-points
GET POST /v1/environmental-readingsUmgebungsüberwachung: Punkte auflisten und Messwerte (auch Batch) einspeisen; analytics_qc-Modul erforderlich.
GET POST /v1/lab-orders
GET /v1/lab-orders/{id}
GET /v1/lab-resultsAufträge mit Proben und freigegebene Ergebnisse; sample_intake-Modul erforderlich.
Die REST-API nimmt Connector-Status, Geräte und Messergebnisse entgegen. Der lokal laufende OPC-UA-/LADS-Agent ist eine separate Integration; diese API stellt keinen universellen Gerätetreiber bereit. Ergebnisse gehen an /v1/results, Rohdateien an /v1/results/upload (multipart/form-data, file und device_id). Größenlimits: 25 MiB, für mzML 50 MiB. API-Keys können an Connector und Geräte gebunden sein; PATs nicht. Automatische Geräteregistrierung beim Ergebnis-Ingest erfordert devices:register oder Administrator-Scope. Gerätespezifische Zugriffe und Ergebniszuordnung müssen vor Einsatz geprüft werden.
Konfigurierte Webhook-Endpunkte abonnieren exakte Ereignisnamen oder Wildcards. Zustellungen erfolgen asynchron an zulässige HTTP(S)-Adressen; interne Netzwerkadressen werden blockiert. Bei Fehlern sind insgesamt bis zu fünf Versuche mit exponentiellem Backoff vorgesehen. Eine feste Zustellzeit wird nicht zugesagt.
experiment.createdLaboreintrag angelegt
id, display_id, title, status, project_id, author_id, created_at
experiment.status_changedStatus geändert
id, display_id, old_status, new_status, updated_at
experiment.signedsigned_at erstmals gesetzt
id, display_id, signed_by, signed_at
sample.createdProbe angelegt
id, name, type, status, storage_location_id, created_at
sample.status_changedProbenstatus geändert
id, name, old_status, new_status, updated_at
stock_movement.createdBewegung angelegt
id, stock_item_id, movement_type, quantity_delta, quantity_after, unit, reason, …
equipment.usage.startedNutzung begonnen
session_id, equipment_id, user_id, started_at, purpose, project_id, entry_id, settings
equipment.usage.completedNutzung beendet
session_id, equipment_id, started_at, ended_at, duration_minutes, auto_closed, …
equipment.maintenance.completedWartung abgeschlossen
log_id, equipment_id, …
Ereignisfilter
experiment.* · sample.* · stock_movement.* · equipment.* · *
id bezeichnet die Zustellung, event den Ereignistyp und data die Ereignisdaten. Empfänger müssen doppelte Zustellungen anhand der Zustell-ID abfangen. Das Beispiel zeigt eine Signatur eines Laboreintrags; IDs und Zeitpunkte sind Beispieldaten.
{
"id": "11111111-1111-4111-8111-111111111111",
"event": "experiment.signed",
"org_id": "22222222-2222-4222-8222-222222222222",
"delivered_at": "2026-10-07T03:00:00Z",
"data": {
"id": "33333333-3333-4333-8333-333333333333",
"display_id": "EXP-0421",
"signed_by": "44444444-4444-4444-8444-444444444444",
"signed_at": "2026-10-07T02:59:58Z"
}
}X-LabNote-Signature enthält t=<Unix-Sekunden>,v1=<Hex-HMAC>. Signiert werden die unveränderten Bytes von <timestamp>.<raw body> mit HMAC-SHA256. Bei Secret-Rotation können zwei v1-Werte erscheinen: Ein gültiger Vergleich genügt. JSON erst nach der Prüfung parsen. Die Beispiele berücksichtigen mehrere Signaturen, fehlerhafte Header und eine Zeittoleranz von ±300 Sekunden.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyLabnoteSignature(rawBody, header, secret,
toleranceSeconds = 300) {
if (typeof header !== "string") return false;
const parts = header.split(",").map(p => p.trim());
const timestamps = parts.filter(p => p.startsWith("t="));
const signatures = parts.filter(p => p.startsWith("v1="))
.map(p => p.slice(3));
if (timestamps.length !== 1 || !signatures.length) return false;
const stamp = timestamps[0].slice(2);
if (!/^[0-9]+$/.test(stamp)) return false;
const ts = Number(stamp);
if (!Number.isSafeInteger(ts) ||
Math.abs(Date.now() / 1000 - ts) > toleranceSeconds) return false;
const expected = createHmac("sha256", secret)
.update(stamp + ".").update(rawBody).digest();
return signatures.some(sig => {
if (!/^[a-f0-9]{64}$/i.test(sig)) return false;
return timingSafeEqual(Buffer.from(sig, "hex"), expected);
});
}import hmac, hashlib, time, re
def verify_labnote_signature(raw_body: bytes, header: str,
secret: str, tolerance: int = 300) -> bool:
if not isinstance(header, str):
return False
parts = [p.strip() for p in header.split(",")]
stamps = [p[2:] for p in parts if p.startswith("t=")]
sigs = [p[3:] for p in parts if p.startswith("v1=")]
if len(stamps) != 1 or not re.fullmatch(r"[0-9]{1,16}", stamps[0]):
return False
if abs(time.time() - int(stamps[0])) > tolerance:
return False
expected = hmac.new(secret.encode(), stamps[0].encode() + b"." +
raw_body, hashlib.sha256).hexdigest()
return any(re.fullmatch(r"[a-fA-F0-9]{64}", sig) and
hmac.compare_digest(expected, sig.lower()) for sig in sigs)Separater POST-Endpunkt: /inbound-webhook/{org_id}/{slug}, nicht unter /api-v1/v1. Authentifizierung über das konfigurierte HMAC-Secret. Der Ereignistyp stammt aus event oder X-LabNote-Event, nicht aus event_type. Maximale Nachrichtengröße: 1.000.000 Bytes. HTTP 202 bestätigt Speicherung bzw. optionales Einreihen eines Jobs, nicht den erfolgreichen fachlichen Import. Nicht erlaubte Ereignisse erhalten ebenfalls 202 mit status: ignored. Ungültige Signaturen und unbekannte oder inaktive Endpunkte liefern einheitlich 404. external_id allein ist kein Deduplizierungsschlüssel; bei Wiederholungen denselben Idempotency-Key verwenden.
// Node.js: sign the exact body sent to the receiver
import { createHmac, randomUUID } from "node:crypto";
const url = "https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/inbound-webhook/<org-uuid>/<slug>";
const body = JSON.stringify({ event: "instrument.measurement_recorded",
payload: { sample_code: "S-001", value: 12.3, unit: "mg/L" } });
const stamp = String(Math.floor(Date.now() / 1000));
const sig = createHmac("sha256", process.env.LABNOTE_WEBHOOK_SECRET)
.update(stamp + "." + body).digest("hex");
const key = randomUUID(); // retain this key and body for retries
const response = await fetch(url, { method: "POST", headers: {
"Content-Type": "application/json",
"X-LabNote-Signature": "t=" + stamp + ",v1=" + sig,
"Idempotency-Key": key
}, body });
console.log(response.status);POST /v1/jobs erwartet job_type und optional input und antwortet mit 202 und data. GET liest den Status. Die Typen audit_export, biosafety_export, gdpr_export und org_data_export erfordern eine Administratorrolle. Andere zugelassene Typen sind equipment_lifecycle_pdf, maintenance_report_pdf, equipment_book_pdf, experiment_entry_pdf, risk_assessment_pdf und box_layout_pdf. Nutzereigene Jobs sind für den Ersteller sichtbar, organisationsweite Einsicht für Administratoren. Annahme eines Jobs garantiert weder erfolgreichen Abschluss noch einen bestimmten Dateivertrag; input hängt vom jeweiligen Job ab.
# Read existing jobs; creation needs job-specific input
curl -sS "$LABNOTE_API/v1/jobs?limit=20" \
-H "Authorization: Bearer $LABNOTE_API_KEY"
# POST body format (replace input with the job's required fields):
# {"job_type":"experiment_entry_pdf","input":{"entry_id":"<entry-uuid>"}}
# Poll an accepted job:
curl -sS "$LABNOTE_API/v1/jobs/<job-uuid>" \
-H "Authorization: Bearer $LABNOTE_API_KEY"POST benötigt write und adressiert Organisationsmitglieder über user_id, user_ids oder role. GET mit read liefert Benachrichtigungen des dem Token zugeordneten Nutzers; dies ist keine organisationsweite Abfrage. Zulässige Links beginnen mit http:// oder https://. Bei begrenzten Empfängerlisten oder Ergebnissen die Warning-Header und Pagination-Angaben auswerten.
curl -sS -X POST "$LABNOTE_API/v1/notifications" \
-H "Authorization: Bearer $LABNOTE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: <stable-unique-request-key>" \
-d '{"title":"K-04 temperature alert","body":"12.3 °C; limit 8 °C",
"notification_type":"temperature_alarm","role":"admin"}'REST-Standardlimits im Code: 120 Anfragen/Minute und 5.000/Stunde pro Token; die Bereitstellung kann andere Werte konfigurieren. Bei diesem Token-Limit liefert 429 aktuell Retry-After: 60, nicht die exakte Restdauer des Stundenfensters. Weitere routenspezifische Limits existieren. Idempotency-Key ist optional und nicht auf allen Schreibpfaden ausgewertet: unterstützt bei generischem CRUD, Jobs, Benachrichtigungen, Geräte-Unterressourcen-POST; nicht pauschal bei Connector-POST, Remote-Zugriff, Messdaten-Ingest oder Geräte-Unterressourcen-PATCH/PUT. Ein Replay behält den ursprünglichen Statuscode und setzt Idempotent-Replay: true; Standardaufbewahrung 24 Stunden.
Generische Listen: limit (Standard 50, max. 500), offset, order_by, order=asc|desc und erlaubte Gleichheitsfilter. Geräte-Unterressourcen: Standard 100, max. 500, from/to. Audit: from/to-Zeitfilter, Standard 100, max. 1.000; next_cursor liefert since_created_at und since_id als nächste Query-Parameter. Jobs: Standard 50, max. 200; cursor ist der Zeitstempel enqueued_at aus next_cursor. Laborergebnisse sind auf 2.000 Zeilen und vorgeschaltete Probenabfragen auf 1.000 begrenzt; Warning-Header beachten. Nicht alle Listen besitzen offset oder einen Cursor.
# Generic list
GET /v1/samples?limit=50&offset=0&order_by=created_at&order=desc
# Audit: use BOTH fields returned in next_cursor
GET /v1/audit?since_created_at=<ISO-timestamp>&since_id=<uuid>&limit=100
# Jobs: cursor is an enqueued_at timestamp
GET /v1/jobs?cursor=<ISO-timestamp>&limit=50X-Test-Mode: true wird ausschließlich im generischen CRUD-Handler ausgewertet. Es prüft dort Anmeldung und vorgelagerte Schreibberechtigungen und gibt would_apply zurück, überspringt jedoch die eigentliche Feldvalidierung und Datensatzänderung. Es ist kein allgemeiner Dry-Run für Jobs, Webhooks oder Messdaten und kein vollständiger Schreibtest. Für echte Integrationstests eine isolierte Testorganisation verwenden. Unterschiedliche Fehlerformate auswerten; X-Request-Id ist für api-v1 nicht generell implementiert.
// Most REST route errors
{"error":{"code":"insufficient_scope","message":"Token lacks 'read' scope."}}
// Shared idempotency conflict (different shape)
{"data":null,"error":"idempotency_key_conflict","meta":{"message":"Same Idempotency-Key used with a different payload."}}Die separaten Funktionen oauth-authorize und oauth-token implementieren einen Authorization-Code-Flow für registrierte Clients mit Client-Secret und rotierenden Refresh-Tokens. Das ist nicht die MCP-Anmeldung. Die Zustimmung bindet den Zugriff an die gewählte Organisation; admin und audit:read werden bei Nutzern ohne Administratorrolle entfernt. Einen öffentlichen PKCE-Flow oder eine allgemeine Freischaltung für beliebige Clients sagt diese Dokumentation nicht zu; Clientregistrierung und Bereitstellung müssen vor einer Integration geklärt werden.
# Separate REST OAuth functions (registered confidential clients)
https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/oauth-authorize
https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/oauth-token
# Not interchangeable with the MCP Supabase OAuth issuer.Im MCP-Modul sind genau vier Werkzeuge definiert: whoami, list_experiments, list_samples und list_equipment. Die Listen sind nutzerbezogen und berechtigungsgefiltert; es gibt keine Schreib- oder Job-Werkzeuge. MCP verwendet Supabase-OAuth mit dem angemeldeten Nutzer, nicht den REST-API-Key. Der Funktionsname ist mcp, nicht mcp-server. Bereitstellung und erfolgreiche autorisierte Aufrufe sind hier nicht als live verifiziert ausgewiesen.
whoamilist_experimentslist_sampleslist_equipment# Defined MCP function; confirm deployment before connecting
https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/mcp
# Discover with an OAuth-capable MCP client: tools/list
# Read-only: whoami, list_experiments, list_samples, list_equipment
# REST API keys are not MCP credentials.Diese Referenz ist öffentlich. Für eine konkrete Anbindung müssen Organisation, Berechtigungen, Clientregistrierung und Testumgebung geklärt sein.