Dokumente freigeben Extended

Die Praxis gibt in Ihrer Software ein Dokument frei — Rechnung, Laborbericht, Impfausweis — und es erscheint in der Petla-App des Tierhalters. Einzeln, widerrufbar, und nie als Abgleich über alles.

Freigabe je Dokument, kein Massenabgleich

Über jede Freigabe entscheidet die Praxis — je Dokument oder über eine Richtlinie, die sie selbst für eine Dokumentart festlegt. Ausgeschlossen ist eine Regel, die Ihre Software oder Petla an ihrer Stelle anwendet, und die Übertragung des gesamten Bestands. Tierärzte sind in § 203 Abs. 1 Nr. 1 StGB ausdrücklich genannt, und eine unbefugte Offenbarung ist für die Praxis strafbewehrt. Befugt ist eine Offenbarung unter anderem dann, wenn der Tierhalter eingewilligt hat; diese Einwilligung holt die Praxis ein, nicht Ihre Software und nicht Petla.

Es gibt kein GET und keine Liste. Nach Abschnitt 2 der Spezifikation erhält Petla über diese Schnittstelle keinen Lesezugriff auf klinische Daten; in der Gegenrichtung gilt dasselbe. Was Sie bereits gesendet haben, steht in Ihrer eigenen Datenbank.

Voraussetzung

Erklären Sie documents.push in PUT /capabilities. Ohne diese Fähigkeit antworten beide Endpunkte mit 400 capability_not_declared — geprüft, bevor der Rumpf gelesen wird, weshalb es ein 400 ist und kein 403: nichts ist verboten, es ist nur nichts angemeldet.

documents.push ist ein neuer Wert in einer offenen Liste, keine neue Version. Ein System, das ihn nicht kennt, ignoriert ihn und muss nichts ändern.

Ein Dokument freigeben

POST /appointments/{appointmentId}/documents
Authorization: Bearer <Verbindungs-Token>
Petla-Clinic-Id: <clinicId>
Content-Type: application/json

{
  "documentId": "INV-2026-0001",
  "kind": "INVOICE",
  "filename": "Rechnung-2026-0001.pdf",
  "note": "Rechnung zum Besuch vom 14.09.",
  "content": "JVBERi0xLjQKJ…"
}
Der Termin ist der Anker

Der Empfänger wird nicht im Rumpf benannt. Petla ermittelt den Tierhalter aus der Buchung; im Rumpf gibt es kein Feld, das einen anderen Empfänger benennen könnte. Damit erreichen Sie die Halter, mit denen ein Termin besteht, in der Praxis, an die diese Verbindung gebunden ist; keine Form der Anfrage erweitert diesen Kreis.

Die Felder

FeldPflichtBedeutung
documentIdjaIhre Kennung, je Verbindung eindeutig. Erlaubt sind A–Z a–z 0–9 . _ : -, bis 128 Zeichen.
filenameja1–200 Zeichen, bei beiden Übertragungswegen. Petla liest keinen Content-Disposition von Ihrem Speicher.
content oder sourceUrlgenau einesDie Datei als Base64 — oder eine https-URL, die Petla einmal abruft.
kindneinINVOICE · LAB_REPORT · DISCHARGE_NOTE · VACCINATION_CERTIFICATE · IMAGING · REFERRAL · OTHER. Vorgabe OTHER.
noteneinBis 500 Zeichen. Die Zeile, die der Halter neben der Datei liest.
petIdneinPetlas Tier-ID, wenn der Besuch mehrere Tiere hatte — Sie haben sie als Appointment.patients[].petlaPetId.
sha256neinPrüfsumme der Datei, Base16.
Genau einer der beiden Wege

Beide zu senden ist ein 400, keinen zu senden ebenfalls. Sind beide vorhanden, ist nicht festgelegt, welcher Weg gälte: ein Absender, der von der URL ausgeht, während Petla die eingebetteten Bytes verwendet, liefert ein Dokument aus, dessen Inhalt er nicht geprüft hat.

kind ist optional, ein verschriebener Wert aber trotzdem ein 400. Fehlt er, heißt das nicht etikettiert, und eine Lücke in der Systematik soll keine Freigabe verhindern; ein Wert außerhalb der Liste heißt, Sie meinten etwas, und Petla weiß nicht was.

sha256 lohnt sich vor allem bei sourceUrl, wo Sie die Bytes nie in der Hand hatten und sonst nicht erfahren, ob Petla das geholt hat, was Sie meinten. Es macht außerdem eine Wiederholung billig: liegt die Prüfsumme vor, wird ein erneuter Versand einer bekannten documentId beantwortet, ohne irgendetwas abzurufen. Passt sie nicht zum Empfangenen, ist es ein 400.

Die Antwort

201 bei der ersten Freigabe. 200 mit demselben Objekt, wenn Kennung und Bytes unverändert sind — das ist die Wiederholung, die Ihr HTTP-Client nach einer Zeitüberschreitung von selbst schickt, und sie ist bewusst kein 409: ein 4xx darf nach § 11 nicht unverändert wiederholt werden, ein PVS hielte seine Freigabe also für gescheitert.

{
  "documentId": "INV-2026-0001",
  "petlaDocumentId": "c0ffee…",
  "appointmentId": "a1b2…",
  "petId": null,
  "kind": "INVOICE",
  "filename": "Rechnung-2026-0001.pdf",
  "mimeType": "application/pdf",
  "sizeBytes": 148213,
  "sha256": "9f86d081…",
  "note": "Rechnung zum Besuch vom 14.09.",
  "releasedAt": "2026-09-14T10:24:31.000Z",
  "revokedAt": null
}

petlaDocumentId ist Petlas eigene Zeilen-ID. Nennen Sie sie in einer Supportanfrage — adressieren lässt sich damit nichts. Jeder Aufruf hier nimmt Ihre documentId.

Grenzen und Ablehnungen

WasHTTPCode
Größer als 10 MB413document_too_large
Nicht PDF, JPEG, PNG oder WebP415document_type_unsupported
Der Termin hat keinen Tierhalter409appointment_has_no_owner
Dieselbe documentId, andere Bytes409document_id_in_use
Diese documentId wurde widerrufen409document_revoked
Ihr Speicher lieferte die sourceUrl nicht aus502document_fetch_failed
Der Typ wird aus den Bytes gelesen

Petla glaubt Ihrer Angabe nicht, sondern prüft die Signatur der Datei — eine umbenannte .pdf ist trivial zu fälschen. Gespeichert und in jedem späteren Content-Type ausgeliefert wird der Typ, den die Bytes belegen. meta.detected nennt, was tatsächlich ankam.

⚠️ document_type_unsupported ist ein anderer Code als unsupported_media_type: der zweite betrifft die Kopfzeile Content-Type der Anfrage, dieser hier den Inhalt.

Die 10 MB gelten für das Dokument; der JSON-Rumpf ist bei 16 MB gedeckelt. Das ist dieselbe Grenze an zwei Stellen gemessen — Base64 bläht um 4/3 auf — und eine Content-Length über der Rumpfgrenze wird abgelehnt, bevor der Rumpf überhaupt gelesen wird.

Wenn Sie sourceUrl benutzen

Petla ruft die URL einmal serverseitig ab. Eine signierte URL eines Objektspeichers erfüllt alle Regeln unten bereits; jede Verletzung ist ein 400 mit fields.sourceUrl und keine Sache, die eine Wiederholung besser macht:

Der gesamte Abruf — DNS, TLS, Kopfzeilen und Rumpf — ist auf 15 Sekunden begrenzt. Eine Zeitüberschreitung oder ein Fehler Ihres Speichers ist ein 502 document_fetch_failed, und den sollten Sie wiederholen: er liegt an keiner der beiden Konfigurationen, während die Regeln oben an Ihrer liegen — das ist genau der Unterschied zwischen 4xx und 5xx hier.

Zwei getrennte Zusicherungen

Der Idempotency-Key sichert eine stabile Antwort zu. Dass keine zweite Zeile entsteht, sichert etwas anderes: (Verbindung, documentId) ist eindeutig, und das gilt unabhängig davon, ob Sie die Kopfzeile senden. Die Eindeutigkeit stammt also aus dem Schlüssel, nicht aus der Kopfzeile.

Ein Dokument widerrufen

POST /appointments/{appointmentId}/documents/{documentId}/revoke
Authorization: Bearer <Verbindungs-Token>
Petla-Clinic-Id: <clinicId>

{ "reason": "Rechnung korrigiert" }

Der Rumpf ist optional — reason ist das einzige Feld, bis 500 Zeichen, wird aufgezeichnet und dem Halter nicht gezeigt. Ein nackter POST ohne Rumpf ist ein gültiger Aufruf und hat folglich auch keinen Content-Type zu deklarieren; was Bytes trägt, muss sie weiterhin als JSON deklarieren.

⚠️ Kein Idempotency-Key. Pfad und Verbindung identifizieren diesen Schreibvorgang bereits eindeutig, eine Wiederholung ist also von Natur aus eine Wiederholung — derselbe Grund, aus dem die Bestätigung keinen nimmt.

Ein Merker, keine Löschung

Der Halter verliert den Zugriff; die Zeile und die Bytes bleiben. Ein Dokument, das offenbart und dann zurückgezogen wurde, ist eine andere Tatsache als eines, das nie offenbart wurde — und unter § 203 StGB ist die zweite diejenige, die nicht erfunden werden darf.

Zwei Eigenschaften des Widerrufs

  1. Der Aufruf ist wiederholbar, und der erste Widerruf gilt. Ein zweiter Aufruf antwortet 200 mit dem ursprünglichen Zeitstempel und dem ursprünglichen Grund — nie mit einem 409 und nie überschreibend. Bei einem 409 hielte ein regelkonformes PVS seinen Widerruf für nicht angekommen, weil § 11 ihm verbietet, den Aufruf unverändert zu wiederholen.

  2. Er gelingt auch für ein Dokument, das Petla nie erhalten hat — Antwort 200 mit status: "not_held". Dieser Fall tritt im Betrieb auf: die Freigabe scheiterte, die Praxis zog das Dokument zurück, und der Widerruf kommt für etwas an, das Petla nicht hält. Ein 404 wäre hier ein 4xx, den Sie nach § 11 nicht unverändert wiederholen dürfen, für eine Anweisung, die Sie anders nicht ausführen können.

{
  "documentId": "INV-2026-0001",
  "appointmentId": "a1b2…",
  "status": "revoked",
  "revokedAt": "2026-09-14T11:02:00.000Z",
  "revokeReason": "Rechnung korrigiert"
}
revoked und not_held sind nicht austauschbar

revoked heißt: Petla hielt das Dokument, und es ist der App des Halters jetzt entzogen. not_held heißt: Petla hat es nie erhalten und den Widerruf vermerkt. Beides ist ein 200, Ihre Anweisung wurde so oder so ausgeführt — aber ein PVS, das seinen eigenen Zustand abgleicht, muss sie unterscheiden können. Zusammengefasst meldeten sie ein Dokument als entzogen aus einer App, die es nie erreicht hat.

Der Vermerk ist auch der Grund, warum not_held mehr ist als eine angenommene Leermeldung. Die gewöhnliche Gestalt einer gescheiterten Freigabe ist eine Wiederholung, die noch in Ihrer eigenen Warteschlange liegt. Ohne den Vermerk könnte sie Minuten später ankommen und einen zurückgezogenen Laborbericht in die App eines Halters legen. Der Vermerk ist das, was der Freigabe erlaubt, diesen späten Nachzügler mit document_revoked abzulehnen.

⚠️ Die Folge: eine widerrufene documentId ist verbraucht. Ein korrigiertes Dokument braucht eine neue — dieselbe Regel wie document_id_in_use, aus der anderen Richtung.

Jeder Code mit Bedeutung und Abhilfe: Fehlercodes.