Erste Schritte

Von null auf einen ersten erfolgreichen Aufruf: zwei Zugangsdaten, eine Fähigkeiten-Erklärung, ein Testsystem.

Wohin Sie senden

Jeder Pfad in dieser Dokumentation ist relativ zur Basis-URL. Die Hauptversion steht darin, nicht in einer Kopfzeile.

UmgebungBasis-URL
Testsystem — hier entwickeln Siehttps://partner-sandbox.petla.app/api/partner/v1
Produktionhttps://vet.petla.app/api/partner/v1

Ein Aufruf ohne gültige Zugangsdaten antwortet mit 401 und dem Fehlerrumpf aus Ereignisse abrufen — das ist der schnellste Weg, Ihre Verbindung zum Testsystem überhaupt zu prüfen:

curl -i https://partner-sandbox.petla.app/api/partner/v1/capabilities

HTTP/2 401
{"code":"unauthorized","status":401,"requestId":"WFMDJTEN"}

Die zwei Zugangsdaten

Sie halten zwei verschiedene Dinge, mit zwei verschiedenen Lebensdauern.

WasGilt fürLebensdauer
Partner-ZugangsdatenSie als Hersteller — einmalig, für alle Praxenlanglebig, rotierbar
Verbindungs-TokenGenau eine Praxis ↔ genau ein Mandant in Ihrem Systembis eine der beiden Seiten sie beendet
Die Partner-Zugangsdaten bekommen Sie von uns

Es gibt dafür keinen Selbstbedienungsweg — schreiben Sie an celine@petla.app. Wir richten Ihre Testpraxis auf dem Testsystem ein und geben Ihnen die Partner-Zugangsdaten dazu. Das Verbindungs-Token stellt danach Ihr System selbst aus, über den Kopplungsablauf unten.

⛔ Die Zugangsdaten gelten je Umgebung. Die Daten, die Sie zum Start bekommen, sind für das Testsystem; gegen die Produktion antworten sie mit 401 unauthorized. Für den Produktivbetrieb stellen wir zum Go-live eigene aus. Rechnen Sie in Ihrer Konfiguration also mit zwei Werten, nicht mit einem.

Nicht tun

Eine Verbindung gehört der PRAXIS, nie einer einzelnen Tierärztin. Kalender und Kundendaten gehören der Praxis; wer sie verlässt, darf die Verbindung nicht mitnehmen.

Eine Praxis verbinden

  1. Die Praxis wählt in Ihrer Software „Mit Petla verbinden“.
  2. Ihr System holt mit den Partner-Zugangsdaten einen kurzlebigen Kopplungscode über POST /connections/pairing-codes und zeigt ihn der Praxis an. Der Rumpf braucht tenantId und practice — beide sind Pflicht:
    {
      "tenantId": "prax-4711",
      "practice": { "name": "Tierarztpraxis Nord", "postalCode": "20095", "city": "Hamburg" }
    }
    practice ist die Beschreibung, die Petla der Praxis auf der Onboarding-Seite anzeigt und in die Anmeldung vorbefüllt — damit sie erkennt, welche Praxis Ihre Software gerade verbindet. ⚠️ Ein automatischer Abgleich gegen die Tierarztfinder-Einträge entsteht daraus nicht; welcher Eintrag der richtige ist, bestätigt die Praxis selbst (siehe unten). Optional sind street, country (ISO 3166-1 alpha-2, Vorgabe DE), email und phone, dazu canonicalId, falls Sie den Eintrag schon kennen.

    Die Antwort trägt vier Felder:

    {
      "pairingCodeId": "8f1c…",
      "pairingCode": "K7QP-2XM4",
      "onboardingUrl": "https://vet.petla.app/onboarding?partner=8f1c…",
      "expiresAt": "2026-09-06T09:15:00.000Z"
    }
  3. Die Praxis gibt diesen Code bei Petla ein. Zeigen Sie Code und onboardingUrl immer beide an — welchen der zwei Wege eine Praxis nimmt, kann Ihre Software nicht wissen, und beide enden an derselben Stelle:
    • Mit Petla-Konto: Sie meldet sich an und tippt den Code unter /settings/partner ein.
    • Ohne Petla-Konto: Sie öffnet onboardingUrl und tippt den Code im zweiten Schritt der Anmeldung ein. Konto, Eintrag und Kopplung entstehen in einem Durchlauf (Eintrag übernehmen und verbinden, siehe unten).
  4. Ihr System fragt den Kopplungscode ab, bis die Praxis ihn eingelöst hat — GET /connections/pairing-codes/{pairingCodeId}, ebenfalls mit den Partner-Zugangsdaten. Dazwischen sitzt ein Mensch, deshalb ist es ein Abruf und keine Antwort.
  5. Dieser Abruf liefert Ihnen das Verbindungs-Token, das für genau diese eine Praxis gilt.
Der Link trägt die pairingCodeId, nie den Code

Die beiden sind nicht gleichwertig: die pairingCodeId ist kein Geheimnis — sie ist die Kennung, mit der Sie abfragen —, der pairingCode ist eines. Ein Geheimnis gehört nicht in eine URL: Die landet im Browserverlauf, im Chatfenster, sobald die Praxis sie an eine Kollegin weiterleitet, in jedem Proxy vor dem Praxisnetz und im Referer jeder Navigation nach außen.

Ein abgeflossener Code gibt außerdem nicht nur Daten preis: er koppelt den PVS-Mandanten der Praxis an die Petla-Praxis eines Fremden, deren Termine dann dorthin fließen. Die kurze Gültigkeit begrenzt das, hebt es aber nicht auf. Der Link trägt deshalb nur, was zum Fortsetzen der Kopplung nötig ist; den Code tippt die Praxis, wie heute unter /settings/partner. Beides zeigen Sie ihr ohnehin an.

⛔ Lesen Sie den Wert, bauen Sie ihn nicht selbst zusammen. Der Host unterscheidet sich je Umgebung, und der Name des Query-Parameters ist nicht Teil der Zusage.

Achtung

onboardingUrl ist ein ergänztes Feld (seit 2026-09-06). Nach §10.5 ignorieren Sie Felder, die Sie nicht kennen, und nach §13 ist Ergänzen keine neue Version — deshalb durfte es ohne Bruch erscheinen, und deshalb lässt eine ältere Auslieferung den Schlüssel schlicht weg. Ihr Client sollte das vertragen, statt daran zu scheitern.

Achtung

Das Token wird genau einmal angezeigt. Petla speichert nur dessen Hash. Wird es neu erzeugt, verliert das vorherige sofort seine Gültigkeit.

Jeder weitere Aufruf

Authorization: Bearer <Verbindungs-Token>
Petla-Clinic-Id: <clinicId>

Ein Verbindungs-Token gilt für genau eine clinicId. Passen die beiden nicht zusammen, antwortet Petla mit connection_clinic_mismatch (403) — nie mit einer stillschweigend erweiterten Berechtigung. Jeder Code mit Bedeutung und Abhilfe: Fehlercodes.

Eintrag übernehmen und verbinden

Rund 12.000 deutsche Praxen existieren bei Petla bereits als nicht beanspruchter Tierarztfinder-Eintrag. Im zweiten Schritt der Anmeldung sucht die Praxis darin nach sich selbst; wählt sie ihren Eintrag aus, werden Übernahme und Verbindung ein Vorgang — sie verlässt ihn mit übernommenem Eintrag und aktiver Buchung.

Findet sie keinen Eintrag, gibt sie die Praxis von Hand ein. Das ist ein regulärer Ausgang und kein Fehlerfall: es entsteht eine neue Praxis, und die Kopplung läuft unverändert weiter. Sie müssen also nicht prüfen, ob Petla die Praxis bereits kennt, bevor Sie einen Kopplungscode anfordern — fordern Sie ihn an und zeigen Sie Code und onboardingUrl an.

Kennen Sie die canonicalId der Praxis im Petla-Verzeichnis, können Sie sie beim Kopplungscode mitgeben. Sie ist ein Hinweis, der den Suchschritt erleichtert, und ersetzt ihn nicht — siehe unten.

Dorthin führt onboardingUrl. Was Petla dort auf dem Bildschirm zeigt — Ihren Namen, die Praxis, wie Sie sie in practice beschrieben haben — kommt aus der Kopplungszeile, nicht aus der URL. ⚠️ Eine mitgegebene canonicalId wird dabei nicht automatisch angewandt: ein Hinweis aus einem PVS ist eine Vermutung darüber, welcher von rund 12.000 Einträgen der richtige ist, und die Praxis bestätigt ihn selbst.

Ihre Fähigkeiten erklären

Sie erklären Petla, was Sie können — Petla fragt Sie nicht ab.

PUT /capabilities

{
  "specVersion": "1.0",
  "capabilities": [
    "availability.publish",
    "appointments.receive",
    "appointments.acknowledge",
    "clients.match"
  ],
  "transport": "poll",
  "availabilityShape": "busy",
  "availabilityIntervalMinutes": 5,
  "bookingGridMinutes": 15,
  "resources": { "selectable": true },
  "appointmentTypes": { "mapped": true }
}
bookingGridMinutes ist NICHT availabilityIntervalMinutes

Die beiden Felder stehen untereinander und meinen Gegensätzliches. availabilityIntervalMinutes sagt, wie oft Sie veröffentlichen. bookingGridMinutes sagt, wie lang ein Termin bei Ihnen ist — also welche Startzeiten Ihr System überhaupt speichern kann.

Beides sind kleine Minutenzahlen, beides ist bei 5, 15 oder 30 plausibel — eine Verwechslung ist deshalb ein 200 und ein still falscher Kalender. Eine Prüfung kann die beiden Werte nicht unterscheiden.

bookingGridMinutes ist optional (lassen Sie es weg, ändert sich nichts) und muss einen 24-Stunden-Tag ganzzahlig teilen — 5, 10, 15, 20, 30, 45, 60, 90 … Alles andere ist ein 400 validation_failed auf genau diesem Feld. Petla bietet dann nur noch Startzeiten an, die auf Ihrem Raster liegen, gezählt ab Mitternacht Ortszeit.

availabilityShape ist verpflichtend

Es entscheidet, welches der beiden Verfügbarkeitsformate Sie senden — busy oder windows — und ist eine eingefrorene Aufzählung. Ohne dieses Feld antwortet Petla mit 400: Ein unbekanntes oder fehlendes Feld ist hier eine Ablehnung und kein Schulterzucken, damit ein Tippfehler nicht erst in der Produktion auffällt. availabilityIntervalMinutes ist optional, sollte aber der Wahrheit entsprechen — Petla bemisst danach Haltedauern und erklärt der Praxis, warum ihre Zeiten veraltet waren.

Achtung

capabilities ist eine offene Liste. Ihr System muss Werte ignorieren, die es nicht kennt, und Petla tut dasselbe. Eine neue Fähigkeit ist deshalb keine brechende Änderung — kompilieren Sie diese Liste nicht in ein switch, das bei Unbekanntem eine Ausnahme wirft.

Die Reihenfolge, in der Sie bauen sollten

  1. Verbindung herstellen und GET /capabilities zurücklesen. Damit steht die Authentifizierung.
  2. Verfügbarkeit veröffentlichen, und zwar im Format busy. Es trägt keinerlei Patientendaten und ist das Leichteste, was Doppelbuchungen verhindert.
  3. Ereignisse abrufen und einen Termin schreiben.
  4. Bestätigen — POST /appointments/{appointmentId}/ack. Erst damit ist der Kreis geschlossen; ohne Bestätigung wartet der Tierhalter.
  5. Danach alles Weitere: Zuordnung von Kunden, Verschiebungen, Reservierungen.
Hinweis

Sie bekommen für die gesamte Entwicklung eine eigene Testpraxis auf der Petla-Staging-Umgebung — mit echten Kunden, Tieren und Terminen, jederzeit zurücksetzbar. Kein Mock: Sie bekommen echte Konflikte und echte Fehler.