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.
Ü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 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
| Feld | Pflicht | Bedeutung |
|---|---|---|
documentId | ja | Ihre Kennung, je Verbindung eindeutig. Erlaubt sind A–Z a–z 0–9 . _ : -, bis 128 Zeichen. |
filename | ja | 1–200 Zeichen, bei beiden Übertragungswegen. Petla liest keinen Content-Disposition von Ihrem Speicher. |
content oder sourceUrl | genau eines | Die Datei als Base64 — oder eine https-URL, die Petla einmal abruft. |
kind | nein | INVOICE · LAB_REPORT · DISCHARGE_NOTE · VACCINATION_CERTIFICATE · IMAGING · REFERRAL · OTHER. Vorgabe OTHER. |
note | nein | Bis 500 Zeichen. Die Zeile, die der Halter neben der Datei liest. |
petId | nein | Petlas Tier-ID, wenn der Besuch mehrere Tiere hatte — Sie haben sie als Appointment.patients[].petlaPetId. |
sha256 | nein | Prüfsumme der Datei, Base16. |
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
| Was | HTTP | Code |
|---|---|---|
| Größer als 10 MB | 413 | document_too_large |
| Nicht PDF, JPEG, PNG oder WebP | 415 | document_type_unsupported |
| Der Termin hat keinen Tierhalter | 409 | appointment_has_no_owner |
Dieselbe documentId, andere Bytes | 409 | document_id_in_use |
Diese documentId wurde widerrufen | 409 | document_revoked |
Ihr Speicher lieferte die sourceUrl nicht aus | 502 | document_fetch_failed |
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:
- nur
httpsund nur Port 443 - keine Zugangsdaten in der URL
- keine Weiterleitungen — ein
3xxwird abgelehnt, nicht verfolgt. Eine verfolgte Weiterleitung führte zu einer zweiten Anfrage an eine ungeprüfte Adresse; eine signierte URL leitet nicht weiter. - der Name muss auf eine öffentliche Adresse auflösen — jede Adresse, auf die er auflöst, geprüft in dem Moment, in dem die Verbindung aufgebaut wird
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.
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.
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
Der Aufruf ist wiederholbar, und der erste Widerruf gilt. Ein zweiter Aufruf antwortet
200mit dem ursprünglichen Zeitstempel und dem ursprünglichen Grund — nie mit einem409und nie überschreibend. Bei einem409hielte ein regelkonformes PVS seinen Widerruf für nicht angekommen, weil § 11 ihm verbietet, den Aufruf unverändert zu wiederholen.Er gelingt auch für ein Dokument, das Petla nie erhalten hat — Antwort
200mitstatus: "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. Ein404wäre hier ein4xx, 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 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.