Versionierung: was sich ändern darf und was nicht
Die Fassung steht im Pfad. Was additiv ist, kommt jederzeit; was bricht, bekommt eine neue Fassung — und wie man eine Anbindung schreibt, die beides übersteht.
Für EntwicklerDIE FASSUNG STEHT IM PFAD: `/api/v1/…`. Sie steht ausserdem in jeder Antwort in der Kopfzeile `TT-Api-Version`. Es gibt keine Versionierung über einen `Accept`-Header und keine Datumsfassungen — eine Adresse, die Sie einmal aufgerufen haben, bedeutet in einem Jahr dasselbe.
WAS JEDERZEIT PASSIEREN DARF (additiv, ohne Ankündigung): ein NEUES FELD in einer Antwort, ein neuer optionaler Abfrageparameter, ein neues optionales Rumpffeld, eine neue Route, ein NEUER WERT in einer Aufzählung und ein neuer Eintrag im Fehlerkatalog. Ihre Anbindung muss das überstehen.
WAS NICHT PASSIERT, solange `v1` steht: ein Feld verschwindet aus einer Antwort, ein Feld ändert seinen Typ oder seine Einheit, ein bisher optionaler Parameter wird Pflicht, eine Route verschwindet, oder ein Fehlercode ändert seine Bedeutung. Wird eine solche Änderung nötig, entsteht `/api/v2`, und `v1` läuft weiter.
DIE DREI REGELN FÜR EINE ANBINDUNG, DIE DAS AUSHÄLT: (1) Lesen Sie nur die Felder, die Sie brauchen, und ignorieren Sie den Rest — kein strenges Schema über die ganze Antwort, das bei einem neuen Feld wirft. (2) Behandeln Sie einen unbekannten Aufzählungswert als „unbekannt" und nicht als Fehler; `status`, `source`, `packageUnit` und `kind` können wachsen. (3) Verzweigen Sie auf `error`, nicht auf `message` — der Wortlaut ist für Menschen.
AUFZÄHLUNGEN SIND OFFEN, wo sie herausgehen, und GESCHLOSSEN, wo sie hereinkommen. Eine Antwort darf einen `status` enthalten, den Sie noch nicht kennen; eine Anfrage mit einem unbekannten Wert wird abgewiesen. Das ist Absicht: ein Tippfehler in `status` soll ein 400 sein und kein stiller Standardwert.
FELDNAMEN SIND DURCHGEHEND camelCase — `partySize`, `updatedSince`, `priceCents`. Auch in Sprachen, die snake_case bevorzugen. Zwei Schreibweisen nebeneinander zwängen jeden Aufrufer zu einer Fallunterscheidung, die nichts bedeutet.
GELDBETRÄGE SIND GANZZAHLIGE CENT, und der Feldname sagt es: `priceCents`, `totalSpentCents`, `packagePriceNetCents`, `totalCostCents`. Nie Gleitkomma, nie Euro. Ein `1200` ist zwölf Euro. Die einzige Ausnahme ist `vatRate` — ein Prozentsatz, kein Betrag.
ZEITEN HABEN DREI FORMEN, und sie werden nicht vermischt. Ein KALENDERTAG ist `"2026-09-20"` und trägt keine Zone — er bezieht sich auf die Zeitzone des Betriebs. Eine NAIVE UHRZEIT ist `"19:30"` und ebenso. Ein ZEITPUNKT ist ISO-8601, meist in UTC mit `Z`; `startsAt` und `endsAt` einer Reservierung tragen stattdessen den Zonenversatz (`2026-09-20T19:30:00+02:00`), damit ein Mensch im JSON die richtige Uhrzeit liest.
Schritt für Schritt
`TT-Api-Version` protokollieren
Einmal je Lauf genügt. Ändert sich der Wert, wissen Sie, warum sich sonst etwas geändert hat.
Antworten locker parsen
In TypeScript: ein Interface mit den Feldern, die Sie brauchen. In Go: eine Struktur mit genau diesen Feldern — `encoding/json` überliest den Rest. In C#/Java: keine Einstellung wählen, die bei unbekannten Feldern wirft.
Unbekannte Aufzählungswerte durchreichen
Speichern Sie den Rohwert und zeigen Sie ihn an, statt ihn auf einen Standardwert abzubilden. Ein unbekannter `status`, der als `PENDING` angezeigt wird, ist eine Falschauskunft an den Wirt.
Kalendertag und Zeitpunkt getrennt halten
Machen Sie aus `"2026-09-20"` NIE einen Zeitpunkt in Ihrer lokalen Zone. Westlich von Greenwich wird daraus der 19. September, und die Abendreservierung rutscht auf den Vortag.
Codebeispiele
curl -sS -D - -o /dev/null https://tactictable.com/api/v1/branding \
-H "Authorization: Bearer tt_live_if3u5vp6maf67xmw_PT1xP8EQF7reKUtLcZ7v9z5qaRKmoxeHmd27JpaDvfM" \
| grep -i "tt-api-version"const BEKANNTE_STATUS = [ 'PENDING', 'CONFIRMED', 'OPTION', 'CANCELED', 'NOSHOW', 'COMPLETED',] as const type BekannterStatus = (typeof BEKANNTE_STATUS)[number] const BESCHRIFTUNG: Record<BekannterStatus, string> = { PENDING: 'Anfrage offen', CONFIRMED: 'Bestätigt', OPTION: 'Vormerkung', CANCELED: 'Storniert', NOSHOW: 'Nicht erschienen', COMPLETED: 'Abgeschlossen',} /** * Ein unbekannter Wert wird DURCHGEREICHT, nicht abgebildet. * Ihn als „Bestaetigt" anzuzeigen waere eine Falschauskunft an den Wirt. */export function statusLabel(wert: string): string { return (BESCHRIFTUNG as Record<string, string>)[wert] ?? wert}package tactictable // Reservierung traegt absichtlich NICHT alle 33 Felder der Antwort.// encoding/json ueberliest alles, was hier nicht steht — damit bricht ein// neu hinzugekommenes Feld der API diese Anbindung nicht.type Reservierung struct { ID string `json:"id"` Status string `json:"status"` Date string `json:"date"` Time string `json:"time"` PartySize int `json:"partySize"` GuestName string `json:"guestName"` TableIDs []string `json:"tableIds"` Timezone string `json:"timezone"` UpdatedAt string `json:"updatedAt"`} type Liste struct { Data []Reservierung `json:"data"` NextCursor *string `json:"nextCursor"` HasMore bool `json:"hasMore"` Limit int `json:"limit"`}from datetime import date, datetimefrom zoneinfo import ZoneInfo def tag_lesen(wert: str) -> date: """`"2026-09-20"` -> ein Datum OHNE Zone. Nie in Ortszeit umrechnen.""" return date.fromisoformat(wert) def zeitpunkt_lesen(wert: str) -> datetime: """`"2026-09-20T19:30:00+02:00"` -> ein echter Zeitpunkt.""" return datetime.fromisoformat(wert) def anzeigen(tag: str, uhrzeit: str, zone: str) -> str: """Kalendertag plus naive Uhrzeit ergeben den Zeitpunkt IM BETRIEB.""" ortszeit = datetime.fromisoformat(tag + "T" + uhrzeit) return ortszeit.replace(tzinfo=ZoneInfo(zone)).isoformat() # anzeigen("2026-09-20", "19:30", "Europe/Vienna") -> 2026-09-20T19:30:00+02:00Siehe auch
- Der erste Aufruf in fünf MinutenSchlüssel anlegen, Rechte setzen, die Stammdaten des Betriebs abrufen — in acht Sprachen, vom fertigen Befehl bis zur vollständigen Antwort.
- Die Form jedes FehlersEin Körper, ein Aufbau, ein geschlossener Katalog von Kennungen — und warum in `message` nie eine rohe Datenbankmeldung steht.
- Reservierungen lesen, anlegen und ändernDas Reservierungsbuch: Liste mit zwanzig Filtern, Einzelabruf mit ETag, Anlegen durch dieselbe Torwache wie das Widget und Teiländerung ohne Datenverlust.