{"openapi": "3.0.3", "info": {"title": "Tierklinik in meiner Nähe API", "version": "1.0.0", "description": "Agent-native Tierklinik-Verzeichnis. Geo-sortierte Suche je PLZ/Koordinaten, mit Buchbarkeit.", "x-versions": [{"name": "v1", "status": "current", "baseUrl": "https://tierklinik-in-meiner-naehe.de/v1"}], "x-deprecation-policy": "Soll ein Endpunkt ersetzt werden, bleibt die alte Version 12 Monate unter /v{N} erreichbar; Deprecation wird über x-deprecation:true am Path-Object + Header markiert.", "x-connector-version": "0.1.0", "x-connector-docs": "https://tierklinik-in-meiner-naehe.de/connector.md"}, "x-versioning": "URL-Pfad-Versionierung unter /v1, /v2, ...", "x-openai-isConsequential": false, "servers": [{"url": "https://tierklinik-in-meiner-naehe.de/v1"}], "paths": {"/v1/search": {"get": {"summary": "Such-Ergebnisse", "description": "Geo-sortierte Suche nach Tierkliniken. Übergib plz (empfohlen), lat/lon oder q.", "operationId": "searchtierklinik", "parameters": [{"name": "q", "in": "query", "description": "Suchbegriff (Name oder Stadt)", "schema": {"type": "string"}}, {"name": "plz", "in": "query", "description": "Deutsche Postleitzahl, z. B. 10115", "schema": {"type": "string"}}, {"name": "lat", "in": "query", "description": "Breitengrad (mit lon)", "schema": {"type": "number"}}, {"name": "lon", "in": "query", "description": "Längengrad (mit lat)", "schema": {"type": "number"}}, {"name": "limit", "in": "query", "description": "Maximale Ergebnisanzahl", "schema": {"type": "integer", "default": 10}}, {"name": "booking", "in": "query", "description": "Nur online-buchbare Betriebe", "schema": {"type": "string", "enum": ["only", "true", "false"]}}], "responses": {"200": {"description": "JSON-Suchergebnisse", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchResult"}}}}, "400": {"$ref": "#/components/responses/Error"}, "404": {"$ref": "#/components/responses/Error"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/records": {"get": {"summary": "Alle Datensätze", "operationId": "recordstierklinik", "responses": {"200": {"description": "Alle Datensätze", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecordList"}}}}, "400": {"$ref": "#/components/responses/Error"}}}}, "/v1/booking-offers": {"get": {"summary": "Online-buchbare Betriebe", "operationId": "bookingOfferstierklinik", "responses": {"200": {"description": "Booking-Angebote", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/BookingList"}}}}}}}, "/v1/nearby": {"get": {"operationId": "findNearby", "summary": "Umkreissuche mit Öffnungsstatus", "tags": ["connector"], "parameters": [{"name": "plz", "in": "query", "schema": {"type": "string", "pattern": "^[0-9]{5}$"}}, {"name": "lat", "in": "query", "schema": {"type": "number"}}, {"name": "lon", "in": "query", "schema": {"type": "number"}}, {"name": "city", "in": "query", "schema": {"type": "string"}}, {"name": "radius_km", "in": "query", "schema": {"type": "number", "default": 5.0, "maximum": 50.0}}, {"name": "limit", "in": "query", "schema": {"type": "integer", "default": 10, "maximum": 50}}, {"name": "at", "in": "query", "schema": {"type": "string", "format": "date-time"}}, {"name": "open_at_time", "in": "query", "schema": {"type": "boolean"}}, {"name": "has_phone", "in": "query", "schema": {"type": "boolean"}}, {"name": "has_website", "in": "query", "schema": {"type": "boolean"}}, {"name": "booking_capable", "in": "query", "schema": {"type": "boolean"}}, {"name": "q", "in": "query", "schema": {"type": "string"}}], "responses": {"200": {"description": "Ergebnisse (status ok | no_results_in_radius | all_closed_at_time)", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"$ref": "#/components/responses/Error"}, "404": {"$ref": "#/components/responses/Error"}, "422": {"$ref": "#/components/responses/Error"}}}}, "/v1/business": {"get": {"operationId": "getBusiness", "summary": "Details zu einer ID", "tags": ["connector"], "parameters": [{"name": "id", "in": "query", "required": true, "schema": {"type": "string"}}, {"name": "at", "in": "query", "schema": {"type": "string", "format": "date-time"}}], "responses": {"200": {"description": "Eintrag", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"$ref": "#/components/responses/Error"}, "404": {"$ref": "#/components/responses/Error"}}}}, "/v1/sandbox/businesses": {"get": {"operationId": "sandboxListSampleBusinesses", "summary": "SANDBOX: fiktive Betriebe + freie Slots", "tags": ["sandbox"], "parameters": [{"name": "date", "in": "query", "schema": {"type": "string", "format": "date"}}], "responses": {"200": {"description": "Fiktive Betriebe", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"$ref": "#/components/responses/Error"}}}}, "/v1/sandbox/holds": {"post": {"operationId": "sandboxHoldSlot", "summary": "SANDBOX: Slot vormerken", "tags": ["sandbox"], "security": [{"bearerAuth": []}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "required": ["business_id", "date", "start_time", "topic"], "properties": {"business_id": {"type": "string"}, "date": {"type": "string", "format": "date"}, "start_time": {"type": "string", "pattern": "^[0-9]{2}:[0-9]{2}$"}, "duration_minutes": {"type": "integer", "enum": [30, 60, 90, 120]}, "topic": {"type": "string", "maxLength": 200}}}}}}, "responses": {"201": {"description": "Hold mit hold_id und expires_at", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"$ref": "#/components/responses/Error"}, "401": {"$ref": "#/components/responses/Error"}, "404": {"$ref": "#/components/responses/Error"}, "409": {"$ref": "#/components/responses/Error"}, "422": {"$ref": "#/components/responses/Error"}}}}, "/v1/sandbox/holds/{hold_id}": {"get": {"operationId": "sandboxGetHold", "summary": "SANDBOX: eigenen Hold abrufen", "tags": ["sandbox"], "security": [{"bearerAuth": []}], "parameters": [{"name": "hold_id", "in": "path", "required": true, "schema": {"type": "string"}}], "responses": {"200": {"description": "Hold", "content": {"application/json": {"schema": {"type": "object"}}}}, "401": {"$ref": "#/components/responses/Error"}, "404": {"$ref": "#/components/responses/Error"}}}}, "/v1/sandbox/appointments": {"post": {"operationId": "sandboxRequestAppointment", "summary": "SANDBOX: Termin anfragen (idempotent)", "tags": ["sandbox"], "security": [{"bearerAuth": []}], "parameters": [{"name": "Idempotency-Key", "in": "header", "schema": {"type": "string", "minLength": 8, "maxLength": 128}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "required": ["hold_id", "user_approved"], "properties": {"hold_id": {"type": "string"}, "user_approved": {"type": "boolean", "enum": [true]}, "idempotency_key": {"type": "string"}}}}}}, "responses": {"201": {"description": "Neuer Sandbox-Termin", "content": {"application/json": {"schema": {"type": "object"}}}}, "200": {"description": "Wiederholte Anfrage: derselbe Termin (replayed=true)", "content": {"application/json": {"schema": {"type": "object"}}}}, "400": {"$ref": "#/components/responses/Error"}, "401": {"$ref": "#/components/responses/Error"}, "404": {"$ref": "#/components/responses/Error"}, "409": {"$ref": "#/components/responses/Error"}, "410": {"$ref": "#/components/responses/Error"}, "422": {"$ref": "#/components/responses/Error"}}}}, "/v1/sandbox/appointments/{appointment_id}": {"get": {"operationId": "sandboxGetAppointment", "summary": "SANDBOX: eigenen Termin abrufen", "tags": ["sandbox"], "security": [{"bearerAuth": []}], "parameters": [{"name": "appointment_id", "in": "path", "required": true, "schema": {"type": "string"}}], "responses": {"200": {"description": "Termin", "content": {"application/json": {"schema": {"type": "object"}}}}, "401": {"$ref": "#/components/responses/Error"}, "404": {"$ref": "#/components/responses/Error"}}}}, "/v1/sandbox/me": {"get": {"operationId": "sandboxMyBookings", "summary": "SANDBOX: eigene Holds und Termine", "tags": ["sandbox"], "security": [{"bearerAuth": []}], "responses": {"200": {"description": "Holds und Termine", "content": {"application/json": {"schema": {"type": "object"}}}}, "401": {"$ref": "#/components/responses/Error"}}}}}, "components": {"responses": {"Error": {"description": "Fehler", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}, "RateLimited": {"description": "Zu viele Anfragen", "headers": {"Retry-After": {"schema": {"type": "integer"}, "description": "Sekunden bis zum nächsten erlaubten Request"}}, "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}}}}}, "schemas": {"Problem": {"type": "object", "required": ["title", "status", "code"], "properties": {"type": {"type": "string"}, "title": {"type": "string", "description": "Menschlich-lesbare Fehlerbeschreibung"}, "status": {"type": "integer"}, "code": {"type": "string", "description": "Maschinenlesbarer Fehlercode"}, "detail": {"type": "string"}, "instance": {"type": "string"}}}, "BusinessRecord": {"type": "object", "properties": {"name": {"type": "string"}, "city": {"type": "string"}, "street": {"type": "string"}, "postcode": {"type": "string"}, "lat": {"type": "number"}, "lon": {"type": "number"}, "phone": {"type": "string"}, "website": {"type": "string"}, "completeness_score": {"type": "number"}, "booking_capable": {"type": "boolean"}, "booking_platform": {"type": "string"}, "booking_url": {"type": "string"}, "distance_km": {"type": "number"}}}, "SearchResult": {"type": "object", "properties": {"meta": {"type": "object"}, "records": {"type": "array", "items": {"$ref": "#/components/schemas/BusinessRecord"}}}}, "RecordList": {"type": "object", "properties": {"count": {"type": "integer"}, "records": {"type": "array", "items": {"$ref": "#/components/schemas/BusinessRecord"}}}}, "BookingList": {"type": "object", "properties": {"meta": {"type": "object"}, "offers": {"type": "array", "items": {"type": "object"}}}}}, "securitySchemes": {"bearerAuth": {"type": "http", "scheme": "bearer", "description": "API-Key nur für sandbox_*-Schreib-/Leseoperationen. Suche/Details ohne Anmeldung."}}}}