Urlaub mit Tieren
Für Entwickler

Schnittstelle für Buchungssysteme

Damit verbindet ein Gastgeber sein eigenes Buchungssystem mit Urlaub mit Tieren, ohne Kalender-Links. Das Buchungssystem schickt uns Belegung und Preise. Wir melden jede Reservierung und Buchung innerhalb einer Minute an einen Webhook, nach der Zahlung mit Name, Kontakt und Anschrift des Gastes. Die Schnittstelle ist in der Testphase und wird je Gastgeber freigeschaltet.

Grundlagen

Adresse
https://www.urlaub-mit-tieren.com/api/v1/. Genau so, mit https und www. Andere Schreibweisen lehnen wir mit 403 ab, ohne Weiterleitung: Über http wäre der Schlüssel schon ungeschützt unterwegs, und beim Weiterleiten wird aus einem PUT oft ein GET.
Anmeldung
Kopfzeile Authorization: Bearer umt_…. Den Schlüssel legt der Gastgeber unter Meine Unterkünfte, Schnittstelle an. Er sieht nur die Unterkünfte dieses Gastgebers.
Format
JSON in UTF-8, Datum als JJJJ-MM-TT, Zeitpunkte nach ISO 8601, Beträge in Euro als Zahl. Der Abreisetag ist nie belegt.
Fehler
401 ohne gültigen Schlüssel, 403 ohne Freischaltung, 404 für fremde Unterkünfte und Vorgänge, 409 von der Bremse (siehe Belegung) und bei Preisen, solange Smoobu die Unterkunft pflegt, 413 bei einem Rumpf über 1.000.000 Bytes (Umlaute zählen doppelt), 422 bei ungültigen Daten mit message und errors je Feld, 429 bei mehr als 60 Aufrufen in der Minute je Schlüssel, davon höchstens 20 mit PUT, je Gastgeber bei mehr als dem Doppelten, und vor der Anmeldung bei mehr als 120 Aufrufen in der Minute von derselben IP-Adresse, egal mit welchem Schlüssel.

Ablauf

  1. Unterkünfte abrufen und je Unterkunft im Buchungssystem unsere Nummer eintragen.
  2. Bei jeder Änderung die vollständige Belegung senden, dazu mindestens einmal am Tag. Bleibt sie länger als einen Tag aus, sehen wir nach.
  3. Preise und Mindestaufenthalt senden, wenn sie sich ändern.
  4. Webhook eintragen und jede Meldung als Reservierung oder Buchung im Buchungssystem anlegen oder aktualisieren.
  5. Buchungen, die von uns kommen, in der Belegung mit unserer Vorgangsnummer als kennung zurückschicken oder weglassen. Sonst sperrt sich eine Reservierung gegen den eigenen Gast.
  6. Kalender-Links, die der Gastgeber bei uns eingetragen hat, und eine Smoobu-Anbindung ruhen, sobald die erste Belegung über die Schnittstelle angekommen ist. Sperren aus ruhenden Links übernehmen wir, wenn die Schnittstelle dieselben Nächte meldet. Danach kann der Gastgeber die Links entfernen.

Unterkünfte

GET /api/v1/unterkuenfte

{
  "unterkuenfte": [
    {
      "id": 1,
      "titel": "Ferienwohnung am Mühlbach",
      "status": "live",
      "ort": "Musterstadt",
      "url": "https://www.urlaub-mit-tieren.com/unterkunft/ferienwohnung-am-muehlbach/",
      "grundpreis": 89.0,
      "mindestaufenthalt": 2,
      "max_personen": 4,
      "belegung_erhalten_am": "2026-10-25T14:02:11+02:00",
      "preise_erhalten_am": null
    }
  ]
}

Zum Prüfen der Anmeldung gibt es dazu GET /api/v1/konto.

Belegung senden

PUT /api/v1/unterkuenfte/{id}/belegung

Immer die vollständige Belegung ab heute. Was nicht darin steht, geben wir frei, aber nur, was diese Schnittstelle gesperrt hat. Sperren, die der Gastgeber bei uns selbst setzt, bleiben stehen. Sperren aus Kalender-Links und Smoobu ruhen neben der Schnittstelle: Sie bleiben stehen, bis der Gastgeber den Link entfernt. Meldet die Schnittstelle dieselben Nächte, übernehmen wir sie, und auf Nächten unserer eigenen Reservierungen und Buchungen geben wir sie frei. Höchstens 1.500 Zeiträume. Was über 1.100 Tage hinausreicht, kappen wir dort. Zusammen höchstens 5.000 Nächte ab heute. Bremse: Würde ein Aufruf mehr als die Hälfte von mindestens 14 gesperrten Nächten auf einmal freigeben, geben wir nichts frei, außer Nächten unserer eigenen Vorgänge, die der Aufruf zum ersten Mal mit unserer Vorgangsnummer meldet, und antworten mit 409. Neue Sperren aus demselben Aufruf übernehmen wir trotzdem, die Antwort nennt sie in gesperrte_tage. Ist das Freigeben gewollt, denselben Aufruf mit "erzwingen": true wiederholen. Hast du gerade einen unserer Vorgänge gelöscht (verfallen oder storniert) und greift deshalb die Bremse, sende erzwingen nur, wenn sich an deiner Liste sonst nichts geändert hat. Sonst gibt es auch frei, was aus gutem Grund festgehalten wird. Bei 5xx oder Zeitüberschreitung einfach erneut senden, der Aufruf lässt sich beliebig oft wiederholen.

{
  "belegt": [
    { "anreise": "2026-11-02", "abreise": "2026-11-09", "kennung": "GS-4711", "notiz": "Booking.com" },
    { "anreise": "2026-12-20", "abreise": "2026-12-27", "kennung": "UMT-26-04012" }
  ]
}

Antwort:

{
  "ok": true,
  "gesperrte_tage": 7,
  "eigene_buchungen": 1,
  "konflikte": []
}

eigene_buchungen zählt Zeiträume mit der Vorgangsnummer einer laufenden Reservierung oder Buchung bei uns. Davon übergehen wir genau die Nächte, die dieser Vorgang bei uns belegt. konflikte nennt je Fall kennung, anreise, abreise, vorgang, status und grund. Bei ueberschneidung fällt ein Zeitraum auf eine unserer Buchungen oder Reservierungen: Entweder fehlt dort unsere Vorgangsnummer, oder der Zeitraum ist doppelt vergeben. Bei abweichung trägt ein Zeitraum unsere Vorgangsnummer, belegt aber Nächte außerhalb dieses Vorgangs, etwa weil er verlängert wurde oder bei uns verfallen ist. Diese Nächte sperren wir. Wir bekommen in beiden Fällen eine Nachricht.

Preise senden

PUT /api/v1/unterkuenfte/{id}/preise

Je Tag Preis für die Nacht, Preis für höchstens zwei Personen und Mindestaufenthalt bei Anreise an diesem Tag. Nur die genannten Tage und Felder ändern sich, null nimmt einen Wert heraus, dann gilt der Grundpreis bzw. der Mindestaufenthalt der Unterkunft. Preise mindestens 1 Euro, Mindestaufenthalt 1 bis 90 Nächte. Höchstens 1.100 Tage je Aufruf, vergangene Tage und Tage nach dem Horizont übergehen wir.

{
  "tage": [
    { "datum": "2026-12-20", "preis": 119.0, "min_naechte": 7 },
    { "datum": "2026-12-21", "preis": 119.0, "preis_paar": 99.0 },
    { "datum": "2027-01-10", "preis": null }
  ]
}

Antwort: {"ok": true, "gesetzte_tage": 3, "uebergangene_tage": 0}

Kalender gegenprüfen

GET /api/v1/unterkuenfte/{id}/kalender?von=2026-11-01&bis=2026-11-30

Unser Kalender, wie Gäste ihn sehen, je Tag mit frei, gesperrt, gebucht, reserviert, quelle (wer sperrt: api, manuell für den Gastgeber bei uns, sonst der andere Kalender), preis, preis_paar und min_naechte. Ohne Angabe die nächsten 90 Tage, höchstens 400.

Reservierungen und Buchungen

Ein Vorgang ist eine Reservierung oder Buchung. Seine id ist unsere Vorgangsnummer und bleibt von der Zusage bis zur Abreise gleich, nach der Zahlung kommt eine eigene buchungsnummer dazu.

reserviert
: Der Gastgeber hat zugesagt, die Zahlung ist offen. Der Zeitraum ist bei uns vorgemerkt, bitte sperren, bis verfallen oder storniert kommt. reserviert_bis ist die Zahlungsfrist und kann sich verschieben, etwa durch eine Nachfrist bei Überweisung, das kommt als buchung.geaendert. Nur der Name des Gastes, noch keine Kontaktdaten.
bestaetigt
: bezahlt. Mit E-Mail, Telefon, Anschrift und Nachricht des Gastes und den Beträgen.
storniert
: bezahlt und danach storniert, der Zeitraum ist frei.
verfallen
: Reservierung ohne Zahlung abgelaufen oder abgesagt, der Zeitraum ist frei. Eine bezahlte Buchung verfällt nie. Fehlt sie bei uns durch einen Fehler, senden wir kein Ereignis und klären es mit dir. Zeigt ein Abruf eine Buchung, die dein System als bezahlt kennt, als reserviert oder verfallen, gib den Zeitraum bitte nicht frei, sondern sprich uns an.

GET /api/v1/buchungen?unterkunft=1&abreise_ab=2026-10-01

GET /api/v1/buchungen/{vorgangsnummer}

Die Liste enthält reservierte, bestätigte und stornierte Vorgänge mit Abreise ab gestern (oder ab abreise_ab, höchstens 400 Tage zurück). Ein einzelner Vorgang ist auch als verfallen abrufbar. Antwortet der Einzelabruf zu einer Nummer, die das Buchungssystem schon kennt, mit 404, ist der Vorgang bei uns gelöscht und der Zeitraum frei. War die Buchung schon bezahlt, gib den Zeitraum auch dann nicht frei, sondern sprich uns an.

{
  "id": "UMT-26-04012",
  "buchungsnummer": "UMT-26-04013",
  "unterkunft_id": 1,
  "status": "bestaetigt",
  "anreise": "2026-12-20",
  "abreise": "2026-12-27",
  "naechte": 7,
  "reserviert_bis": null,
  "personen": 3,
  "kinder_alter": [6],
  "tiere": [ { "art": "hund", "anzahl": 1, "groessen": ["mittel"] } ],
  "tiere_text": "1× Hund (mittel, bis 25 kg)",
  "gast": {
    "name": "Anna Beispiel",
    "email": "anna@example.org",
    "telefon": "+49 170 0000000",
    "anschrift": { "strasse": "Hauptstraße 1", "plz": "97222", "ort": "Musterstadt", "land": "DE" }
  },
  "nachricht": "Wir kommen gegen 16 Uhr an.",
  "betraege": {
    "waehrung": "EUR",
    "unterkunft": 833.0,
    "kurtaxe": 0.0,
    "spende": 0.0,
    "gast_gezahlt": 833.0,
    "erstattet": 0.0,
    "provision": 108.29,
    "auszahlung": 724.71
  },
  "link": "https://www.urlaub-mit-tieren.com/dashboard/anfrage/412/gespraech/",
  "erstellt_am": "2026-10-25T10:14:03+02:00",
  "geaendert_am": "2026-10-25T10:31:47+02:00"
}

Bei einer Reservierung stehen in betraege nur unterkunft, kurtaxe, spende und gast_zahlt. spende ist die freiwillige Spende des Gastes für den Tierschutz, sie geht nicht an den Gastgeber. auszahlung ist, was an den Gastgeber geht, Kurtaxe eingeschlossen. Nach einem Storno ohne Kontaktdaten, das Buchungssystem hat sie aus der Bestätigung.

Webhook

Bei jeder Änderung schicken wir ein POST an die Adresse, die der Gastgeber einträgt (nur https auf Port 443, ohne Zugangsdaten in der Adresse, nur öffentlich erreichbare Server). Typen: buchung.reserviert, buchung.bestaetigt, buchung.geaendert, buchung.storniert, buchung.verfallen, dazu test vom Knopf im Gastgeberbereich. In buchung steht der Vorgang wie oben, und zwar im neuesten Stand. Ist ein Vorgang bei uns gelöscht (der Gast hat seinen Daten widersprochen), steht darin nur id, unterkunft_id, geaendert_am und status mit storniert oder verfallen: Zeitraum freigeben, außer die Buchung war schon bezahlt. Maßgeblich ist deshalb buchung.status, nicht der Typ: Mit der id als Schlüssel anlegen oder aktualisieren, dann ist auch eine doppelte Meldung kein Problem.

{
  "id": 57,
  "typ": "buchung.bestaetigt",
  "erstellt_am": "2026-10-25T10:31:52+02:00",
  "buchung": { "id": "UMT-26-04012", "status": "bestaetigt", "...": "..." }
}

Jede Meldung trägt X-UMT-Signatur: t=<Unixzeit>,v1=<HMAC>, gebildet als HMAC-SHA256 über Unixzeit + "." + Rumpf mit dem Webhook-Secret. Prüfen in PHP:

$rumpf = file_get_contents('php://input');
preg_match('/^t=(\d+),v1=([0-9a-f]{64})$/', $_SERVER['HTTP_X_UMT_SIGNATUR'] ?? '', $m);
$soll = $m ? hash_hmac('sha256', $m[1].'.'.$rumpf, $secret) : '';

if (! $m || ! hash_equals($soll, $m[2]) || abs(time() - (int) $m[1]) > 300) {
    http_response_code(400);
    exit;
}

$ereignis = json_decode($rumpf, true);
// $ereignis['buchung']['id'] anlegen oder aktualisieren, $ereignis['buchung']['status'] übernehmen
http_response_code(200);

Erwartet wird eine 2xx-Antwort innerhalb von acht Sekunden, Weiterleitungen folgen wir nicht. Die Antwort lesen wir nicht, sie darf höchstens 64 KB groß sein. Sonst versuchen wir es nach 1, 2, 5, 10, 30 und 60 Minuten und dann in größer werdenden Abständen, zusammen gut 30 Stunden. Auf 4xx (außer 408 und 429) folgen nur drei Versuche, denn eine Absage ändert sich durch Warten nicht. Meldungen zu einem Vorgang kommen in ihrer Reihenfolge: Hängt eine, warten die späteren zu diesem Vorgang, Meldungen zu anderen Vorgängen kommen weiter. Holst du daneben selbst ab, kann ein älterer Stand nach einem neueren ankommen: Maßgeblich ist der mit dem späteren geaendert_am. Was wir aufgeben, bleibt über /ereignisse abrufbar.

Ereignisse nachholen

GET /api/v1/ereignisse?nach=57&limit=100

Alle Ereignisse nach einer Nummer, in derselben Form wie der Webhook, höchstens 200 je Aufruf. weitere: true heißt, dass noch mehr da sind. Damit lässt sich auch ganz ohne Webhook arbeiten, dann am besten jede Minute abfragen. Lässt sich ein Vorgang gerade nicht abrufen, steht dort "buchung": null (der Webhook wartet dann und versucht es später). Dann nichts ändern, nach der Reparatur kommt ein neues Ereignis mit dem Stand. In beiden Wegen steht dort null, wenn die Unterkunft nicht mehr zu deinem Konto gehört oder eine schon bezahlte Buchung bei uns fehlt.

Du willst dein Buchungssystem anbinden?

Die Schnittstelle ist in der Testphase, wir schalten sie je Gastgeber frei. Schreib uns, welches Buchungssystem du nutzt, dann melden wir uns.